Skip to content

Commit

Permalink
Merge pull request #214 from glennsarti/refactor-factor
Browse files Browse the repository at this point in the history
(GH-213) Gather facts using the Sidecar
  • Loading branch information
jpogran authored Jan 17, 2020
2 parents 4d36a94 + 82f768b commit 590804e
Show file tree
Hide file tree
Showing 13 changed files with 219 additions and 1 deletion.
36 changes: 36 additions & 0 deletions lib/puppet-languageserver-sidecar/facter_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# frozen_string_literal: true

module PuppetLanguageServerSidecar
module FacterHelper
def self.current_environment
begin
env = Puppet.lookup(:environments).get!(Puppet.settings[:environment])
return env unless env.nil?
rescue Puppet::Environments::EnvironmentNotFound
PuppetLanguageServerSidecar.log_message(:warning, "[FacterHelper::current_environment] Unable to load environment #{Puppet.settings[:environment]}")
rescue StandardError => e
PuppetLanguageServerSidecar.log_message(:warning, "[FacterHelper::current_environment] Error loading environment #{Puppet.settings[:environment]}: #{e}")
end
Puppet.lookup(:current_environment)
end

def self.retrieve_facts(_cache, _options = {})
require 'puppet/indirector/facts/facter'

PuppetLanguageServerSidecar.log_message(:debug, '[FacterHelper::retrieve_facts] Starting')
facts = PuppetLanguageServer::Sidecar::Protocol::Facts.new
begin
req = Puppet::Indirector::Request.new(:facts, :find, 'language_server', nil, environment: current_environment)
result = Puppet::Node::Facts::Facter.new.find(req)
facts.from_h!(result.values)
rescue StandardError => e
PuppetLanguageServerSidecar.log_message(:error, "[FacterHelper::_load_facts] Error loading facts #{e.message} #{e.backtrace}")
rescue LoadError => e
PuppetLanguageServerSidecar.log_message(:error, "[FacterHelper::_load_facts] Error loading facts (LoadError) #{e.message} #{e.backtrace}")
end

PuppetLanguageServerSidecar.log_message(:debug, "[FacterHelper::retrieve_facts] Finished loading #{facts.keys.count} facts")
facts
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,21 @@ def modulepath
result
end
end

# Inject the workspace into the facter search paths
require 'puppet/indirector/facts/facter'
class Puppet::Node::Facts::Facter # rubocop:disable Style/ClassAndModuleChildren
class << self
alias_method :original_setup_search_paths, :setup_search_paths
def setup_search_paths(request)
result = original_setup_search_paths(request)
return result unless PuppetLanguageServerSidecar::Workspace.has_module_metadata?

additional_dirs = %w[lib plugins].map { |path| File.join(PuppetLanguageServerSidecar::Workspace.root_path, path, 'facter') }
.select { |path| FileTest.directory?(path) }

return result if additional_dirs.empty?
Facter.search(*additional_dirs)
end
end
end
21 changes: 21 additions & 0 deletions lib/puppet-languageserver/sidecar_protocol.rb
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,27 @@ def list_for_object_class(klass)
raise "Unknown object class #{klass.name}"
end
end

class Facts < Hash
include Base

def from_h!(value)
value.keys.each { |key| self[key] = value[key] }
self
end

def to_json(*options)
::JSON.generate(to_h, options)
end

def from_json!(json_string)
obj = ::JSON.parse(json_string)
obj.each do |key, value|
self[key] = value
end
self
end
end
end
end
end
10 changes: 10 additions & 0 deletions lib/puppet_languageserver_sidecar.rb
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ def self.require_gems(options)
puppet_parser_helper
sidecar_protocol_extensions
workspace
facter_helper
]

# Load files based on feature flags
Expand Down Expand Up @@ -117,6 +118,7 @@ def self.require_gems(options)
workspace_datatypes
workspace_functions
workspace_types
facts
].freeze

class CommandLineParser
Expand Down Expand Up @@ -389,6 +391,14 @@ def self.execute(options)
PuppetLanguageServerSidecar::PuppetHelper.retrieve_types(null_cache)
end

when 'facts'
# Can't cache for facts
cache = PuppetLanguageServerSidecar::Cache::Null.new
# Inject the workspace etc. if present
injected = inject_workspace_as_module
inject_workspace_as_environment unless injected
PuppetLanguageServerSidecar::FacterHelper.retrieve_facts(cache)

else
log_message(:error, "Unknown action #{options[:action]}. Expected one of #{ACTION_LIST}")
end
Expand Down
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Facter.add('fixture_agent_custom_fact') do
setcode do
'fixture_agent_custom_fact_value'
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
fixture_environment_external_fact: "fixture_environment_external_fact_value"
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Facter.add('fixture_environment_custom_fact') do
setcode do
'fixture_environment_custom_fact_value'
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
fixture_module_external_fact: "fixture_module_external_fact_value"
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Facter.add('fixture_module_custom_fact') do
setcode do
'fixture_module_custom_fact_value'
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
require 'spec_helper'
require 'open3'

describe 'PuppetLanguageServerSidecar::FacterHelper' do
let(:subject) { PuppetLanguageServerSidecar::FacterHelper }

def run_sidecar(cmd_options)
# Use a new array so we don't affect the original cmd_options)
cmd = cmd_options.dup

# Append the puppet test-fixtures
cmd << '--puppet-settings'
cmd << "--vardir,#{File.join($fixtures_dir, 'real_agent', 'cache')},--confdir,#{File.join($fixtures_dir, 'real_agent', 'confdir')}"

cmd.unshift('puppet-languageserver-sidecar')
cmd.unshift('ruby')
stdout, _stderr, status = Open3.capture3(*cmd)

raise "Expected exit code of 0, but got #{status.exitstatus} #{_stderr}" unless status.exitstatus.zero?
return stdout.bytes.pack('U*')
end

let(:default_fact_names) { ['hostname', 'fixture_agent_custom_fact'] }
let(:module_fact_names) { ['fixture_module_custom_fact', 'fixture_module_external_fact'] }
let(:environment_fact_names) { ['fixture_environment_custom_fact', 'fixture_environment_external_fact'] }

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

it 'should return a deserializable facts object with all default facts' do
result = run_sidecar(cmd_options)
deserial = PuppetLanguageServer::Sidecar::Protocol::Facts.new
expect { deserial.from_json!(result) }.to_not raise_error

default_fact_names.each do |name|
expect(deserial).to include(name)
end

module_fact_names.each do |name|
expect(deserial).not_to include(name)
end
end
end

context 'given a workspace containing a module' do
# Test fixtures used is fixtures/valid_module_workspace
let(:workspace) { File.join($fixtures_dir, 'valid_module_workspace') }

describe 'when running facts action' do
let (:cmd_options) { ['--action', 'facts', '--local-workspace', workspace] }

it 'should return a deserializable facts object with default facts and workspace facts' do
result = run_sidecar(cmd_options)
deserial = PuppetLanguageServer::Sidecar::Protocol::Facts.new
expect { deserial.from_json!(result) }.to_not raise_error

default_fact_names.each do |name|
expect(deserial).to include(name)
end

module_fact_names.each do |name|
expect(deserial).to include(name)
end
end
end
end

context 'given a workspace containing an environment.conf' do
# Test fixtures used is fixtures/valid_environment_workspace
let(:workspace) { File.join($fixtures_dir, 'valid_environment_workspace') }

describe 'when running facts action' do
let (:cmd_options) { ['--action', 'facts', '--local-workspace', workspace] }

it 'should return a deserializable facts object with default facts and workspace facts' do
result = run_sidecar(cmd_options)
deserial = PuppetLanguageServer::Sidecar::Protocol::Facts.new
expect { deserial.from_json!(result) }.to_not raise_error

default_fact_names.each do |name|
expect(deserial).to include(name)
end

environment_fact_names.each do |name|
expect(deserial).to include(name)
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
deserial = subject_klass.new.from_json!(serial)

subject.keys.each do |key|
expect(deserial[key]).to eq(deserial[key])
expect(deserial[key]).to eq(subject[key])
end
end
end
Expand Down Expand Up @@ -131,6 +131,30 @@
end
end

describe 'Facts' do
let(:subject_klass) { PuppetLanguageServer::Sidecar::Protocol::Facts }
let(:subject) {
value = subject_klass.new
value['val1_' + rand(1000).to_s] = rand(1000).to_s
value['val2_' + rand(1000).to_s] = rand(1000).to_s
value['val3_' + rand(1000).to_s] = rand(1000).to_s
value
}

it_should_behave_like 'a base Sidecar Protocol object'

describe '#from_json!' do
it "should deserialize a serialized value" do
serial = subject.to_json
deserial = subject_klass.new.from_json!(serial)

subject.keys.each do |key|
expect(deserial[key]).to eq(subject[key])
end
end
end
end

describe 'NodeGraph' do
let(:subject_klass) { PuppetLanguageServer::Sidecar::Protocol::NodeGraph }
let(:subject) {
Expand Down

0 comments on commit 590804e

Please sign in to comment.