puppet-repl 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,69 @@
1
+ module PuppetRepl
2
+ module Exception
3
+ class Error < StandardError
4
+ attr_accessor :data
5
+ def initialize(data={})
6
+ @data = data
7
+ end
8
+
9
+ end
10
+
11
+ class FatalError < Error
12
+ end
13
+
14
+ class ConnectError < Error
15
+ def message
16
+ out = <<-EOF
17
+ #{data[:message]}
18
+ EOF
19
+ end
20
+ end
21
+
22
+ class UndefinedNode < FatalError
23
+ def message
24
+ out = <<-EOF
25
+ Cannot find node with name: #{data[:name]} on remote server
26
+ EOF
27
+ end
28
+ end
29
+
30
+ class TimeOutError < Error
31
+ #Errno::ETIMEDOUT
32
+ end
33
+
34
+ class NoClassError < FatalError
35
+ def message
36
+ out = <<-EOF
37
+ #{data[:message]}
38
+ You are missing puppet classes that are required for compilation.
39
+ Please ensure these classes are installed on this machine in any of the following paths:
40
+ #{data[:default_modules_paths]}
41
+ EOF
42
+ end
43
+ end
44
+
45
+ class NodeDefinitionError < FatalError
46
+ def message
47
+ out = <<-EOF
48
+ You are missing a default node definition in your site.pp that is required for compilation.
49
+ Please ensure you have at least the following default node definition
50
+ node default {
51
+ # include classes here
52
+ }
53
+ in your #{data[:default_site_manifest]} file.
54
+ EOF
55
+ out.fatal
56
+ end
57
+ end
58
+
59
+ class AuthError < FatalError
60
+ def message
61
+ out = <<-EOF
62
+ #{data[:message]}
63
+ You will need to edit your auth.conf or conf.d/auth.conf (puppetserver) to allow node calls.
64
+ EOF
65
+ end
66
+ end
67
+
68
+ end
69
+ end
@@ -22,6 +22,15 @@ module PuppetRepl
22
22
  end
23
23
  @facts
24
24
  end
25
+
26
+ def server_facts
27
+ data = {}
28
+ data["servername"] = Facter.value("fqdn")
29
+ data['serverip'] = Facter.value("ipaddress")
30
+ data["serverversion"] = Puppet.version.to_s
31
+ data
32
+ end
33
+
25
34
  end
26
35
  end
27
36
  end
@@ -1,41 +1,83 @@
1
1
  module PuppetRepl
2
2
  module Support
3
3
  module InputResponders
4
+
5
+ def static_responder_list
6
+ ["exit", "functions", "classification","vars", "krt", "facts",
7
+ "resources", "classes", "play","reset", "help"
8
+ ]
9
+ end
10
+
4
11
  def help(args=[])
5
12
  PuppetRepl::Cli.print_repl_desc
6
13
  end
7
14
 
15
+ def handle_set(input)
16
+ output = ''
17
+ args = input.split(' ')
18
+ args.shift # throw away the set
19
+ case args.shift
20
+ when /node/
21
+ if name = args.shift
22
+ output = "Resetting to use node #{name}"
23
+ reset
24
+ set_remote_node_name(name)
25
+ else
26
+ out_buffer.puts "Must supply a valid node name"
27
+ end
28
+ when /loglevel/
29
+ if level = args.shift
30
+ @log_level = level
31
+ set_log_level(level)
32
+ output = "loglevel #{Puppet::Util::Log.level} is set"
33
+ end
34
+ end
35
+ output
36
+ end
37
+
8
38
  def facts(args=[])
9
- # convert symbols to keys
10
- vars = node.facts.values
11
- ap(vars, {:sort_keys => true, :indent => -1})
39
+ variables = node.facts.values
40
+ variables.ai({:sort_keys => true, :indent => -1})
12
41
  end
13
42
 
14
43
  def functions(args=[])
15
- puts function_map.keys.sort
44
+ filter = args.first || ''
45
+ function_map.keys.sort.grep(/^#{Regexp.escape(filter)}/)
16
46
  end
17
47
 
18
48
  def vars(args=[])
19
49
  # remove duplicate variables that are also in the facts hash
20
- vars = scope.to_hash.delete_if {| key, value | node.facts.values.key?(key) }
21
- vars['facts'] = 'removed by the puppet-repl' if vars.key?('facts')
22
- ap 'Facts were removed for easier viewing'
23
- ap(vars, {:sort_keys => true, :indent => -1})
50
+ variables = scope.to_hash.delete_if {| key, value | node.facts.values.key?(key) }
51
+ variables['facts'] = 'removed by the puppet-repl' if variables.key?('facts')
52
+ output = "Facts were removed for easier viewing".ai + "\n"
53
+ output += variables.ai({:sort_keys => true, :indent => -1})
24
54
  end
25
55
 
26
56
  def environment(args=[])
27
- puts "Puppet Environment: #{puppet_env_name}"
57
+ "Puppet Environment: #{puppet_env_name}"
28
58
  end
29
59
 
30
60
  def reset(args=[])
31
61
  set_scope(nil)
32
- # initilize scope again
33
- scope
62
+ set_remote_node_name(nil)
63
+ set_node(nil)
64
+ set_facts(nil)
34
65
  set_log_level(log_level)
35
66
  end
36
67
 
68
+ def set_log_level(level)
69
+ Puppet::Util::Log.level = level.to_sym
70
+ buffer_log = Puppet::Util::Log.newdestination(:buffer)
71
+ if buffer_log
72
+ # if this is already set the buffer_log is nil
73
+ buffer_log.out_buffer = out_buffer
74
+ buffer_log.err_buffer = out_buffer
75
+ end
76
+ nil
77
+ end
78
+
37
79
  def krt(args=[])
38
- ap(known_resource_types, {:sort_keys => true, :indent => -1})
80
+ known_resource_types.ai({:sort_keys => true, :indent => -1})
39
81
  end
40
82
 
41
83
  def play(args=[])
@@ -44,20 +86,24 @@ module PuppetRepl
44
86
  play_back(config)
45
87
  end
46
88
 
89
+ def classification(args=[])
90
+ node.classes.ai
91
+ end
92
+
47
93
  def resources(args=[])
48
94
  res = scope.compiler.catalog.resources.map do |res|
49
95
  res.to_s.gsub(/\[/, "['").gsub(/\]/, "']") # ensure the title has quotes
50
96
  end
51
97
  if !args.first.nil?
52
- ap res[args.first.to_i]
98
+ res[args.first.to_i].ai
53
99
  else
54
- puts "Resources not shown in any specific order".warning
55
- ap res
100
+ output = "Resources not shown in any specific order\n".warning
101
+ output += res.ai
56
102
  end
57
103
  end
58
104
 
59
105
  def classes(args=[])
60
- ap scope.compiler.catalog.classes
106
+ scope.compiler.catalog.classes.ai
61
107
  end
62
108
 
63
109
  end
@@ -1,14 +1,40 @@
1
+ require 'puppet/indirector/node/rest'
2
+
1
3
  module PuppetRepl
2
4
  module Support
3
5
  module Node
4
- # creates a node object
6
+ # creates a node object using defaults or gets the remote node
7
+ # object if the remote_node_name is defined
5
8
  def create_node
6
- options = {}
7
- options[:parameters] = default_facts.values
8
- options[:facts] = default_facts
9
- options[:classes] = []
10
- options[:environment] = puppet_environment
11
- Puppet::Node.new(default_facts.values['fqdn'], options)
9
+ Puppet[:trusted_server_facts] = true if Puppet.version.to_f >= 4.1
10
+
11
+ if remote_node_name
12
+ # refetch
13
+ node_obj = set_node_from_name(remote_node_name)
14
+ end
15
+ unless node_obj
16
+ options = {}
17
+ options[:parameters] = default_facts.values
18
+ options[:facts] = default_facts
19
+ options[:classes] = []
20
+ options[:environment] = puppet_environment
21
+ node_obj = Puppet::Node.new(default_facts.values['fqdn'], options)
22
+ node_obj.add_server_facts(server_facts) if node_obj.respond_to?(:add_server_facts)
23
+ node_obj
24
+ end
25
+ node_obj
26
+ end
27
+
28
+ def set_remote_node_name(name)
29
+ @remote_node_name = name
30
+ end
31
+
32
+ def remote_node_name=(name)
33
+ @remote_node_name = name
34
+ end
35
+
36
+ def remote_node_name
37
+ @remote_node_name
12
38
  end
13
39
 
14
40
  # @return [node] puppet node object
@@ -16,6 +42,45 @@ module PuppetRepl
16
42
  @node ||= create_node
17
43
  end
18
44
 
45
+ def get_remote_node(name)
46
+ indirection = Puppet::Indirector::Indirection.instance(:node)
47
+ indirection.terminus_class = 'rest'
48
+ remote_node = indirection.find(name, :environment => puppet_environment)
49
+ remote_node
50
+ end
51
+
52
+ # this is a hack to get around that the puppet node fact face does not return
53
+ # a proper node object with the facts hash populated
54
+ # returns a node object with a proper facts hash
55
+ def convert_remote_node(remote_node)
56
+ options = {}
57
+ # remove trusted data as it will later get populated during compilation
58
+ parameters = remote_node.parameters.dup
59
+ trusted_data = parameters.delete('trusted')
60
+ options[:parameters] = parameters || {}
61
+ options[:facts] = Puppet::Node::Facts.new(remote_node.name,remote_node.parameters)
62
+ options[:classes] = remote_node.classes
63
+ options[:environment] = puppet_environment
64
+ node_object = Puppet::Node.new(remote_node.name, options)
65
+ node_object.add_server_facts(server_facts) if node_object.respond_to?(:add_server_facts)
66
+ node_object.trusted_data = trusted_data
67
+ node_object
68
+ end
69
+
70
+ # query the remote puppet server and retrieve the node object
71
+ #
72
+ def set_node_from_name(name)
73
+ out_buffer.puts ("Fetching node #{name}")
74
+ remote_node = get_remote_node(name)
75
+ if remote_node and remote_node.parameters.empty?
76
+ remote_node_name = nil # clear out the remote name
77
+ raise PuppetRepl::Exception::UndefinedNode.new(:name => remote_node.name)
78
+ end
79
+ remote_node_name = remote_node.name
80
+ node_object = convert_remote_node(remote_node)
81
+ set_node(node_object)
82
+ end
83
+
19
84
  def set_node(value)
20
85
  @node = value
21
86
  end
@@ -9,23 +9,45 @@ module PuppetRepl
9
9
  elsif File.exists? config[:play]
10
10
  play_back_string(File.read(config[:play]))
11
11
  else config[:play]
12
- puts "puppet-repl can't play #{config[:play]}'"
12
+ out_buffer.puts "puppet-repl can't play #{config[:play]}'"
13
13
  end
14
14
  end
15
15
  end
16
16
 
17
- def play_back_url(url)
18
- require 'open-uri'
19
- require 'net/http'
17
+ def convert_to_text(url)
18
+ require 'uri'
19
+ url_data = URI(url)
20
+ case url_data.host
21
+ when /^github.com/
22
+ if url_data.path =~ /blob/
23
+ url.gsub('blob', 'raw')
24
+ end
25
+ when /^gist.github.com/
26
+ unless url_data.path =~ /raw/
27
+ url = url += '.txt'
28
+ end
29
+ when /^gitlab.com/
30
+ if url_data.path =~ /snippets/
31
+ url += '/raw' unless url_data.path =~ /raw/
32
+ url
33
+ else
34
+ url.gsub('blob', 'raw')
35
+ end
36
+ else
37
+ url
38
+ end
39
+ end
20
40
 
21
- if url[/gist.github.com\/[a-z\d]+$/]
22
- url += '.txt'
23
- elsif url[/github.com.*blob/]
24
- url.sub!('blob', 'raw')
41
+ def play_back_url(url)
42
+ begin
43
+ require 'open-uri'
44
+ require 'net/http'
45
+ converted_url = convert_to_text(url)
46
+ str = open(converted_url).read
47
+ play_back_string(str)
48
+ rescue SocketError
49
+ abort "puppet-repl can't play `#{converted_url}'"
25
50
  end
26
- play_back_string open(url).read
27
- rescue SocketError
28
- abort "puppet-repl can't play `#{url}'"
29
51
  end
30
52
 
31
53
  def play_back_string(str)
@@ -1,7 +1,6 @@
1
1
  module PuppetRepl
2
2
  module Support
3
3
  module Scope
4
-
5
4
  def set_scope(value)
6
5
  @scope = value
7
6
  end
@@ -9,22 +8,27 @@ module PuppetRepl
9
8
  # @return [Scope] puppet scope object
10
9
  def scope
11
10
  unless @scope
12
- do_initialize
13
- @scope ||= create_scope(node)
11
+ @scope ||= create_scope
14
12
  end
15
13
  @scope
16
14
  end
17
15
 
18
- def create_scope(node)
19
- @compiler = create_compiler(node) # creates a new compiler for each scope
20
- scope = Puppet::Parser::Scope.new(@compiler)
21
- # creates a node class
22
- scope.source = Puppet::Resource::Type.new(:node, node.name)
23
- scope.parent = @compiler.topscope
24
- load_lib_dirs
25
- # compiling will load all the facts into the scope
26
- # without this step facts will not get resolved
27
- scope.compiler.compile # this will load everything into the scope
16
+ def create_scope
17
+ do_initialize
18
+ begin
19
+ @compiler = create_compiler(node) # creates a new compiler for each scope
20
+ scope = Puppet::Parser::Scope.new(@compiler)
21
+ # creates a node class
22
+ scope.source = Puppet::Resource::Type.new(:node, node.name)
23
+ scope.parent = @compiler.topscope
24
+ load_lib_dirs
25
+ # compiling will load all the facts into the scope
26
+ # without this step facts will not get resolved
27
+ scope.compiler.compile # this will load everything into the scope
28
+ rescue StandardError => e
29
+ err = parse_error(e)
30
+ raise err
31
+ end
28
32
  scope
29
33
  end
30
34
 
@@ -16,6 +16,31 @@ module PuppetRepl
16
16
  include PuppetRepl::Support::InputResponders
17
17
  include PuppetRepl::Support::Play
18
18
 
19
+ # parses the error type into a more useful error message defined in errors.rb
20
+ # returns new error object or the original if error cannot be parsed
21
+ def parse_error(error)
22
+ case error
23
+ when SocketError
24
+ PuppetRepl::Exception::ConnectError.new(:message => "Unknown host: #{Puppet[:server]}")
25
+ when Net::HTTPError
26
+ PuppetRepl::Exception::AuthError.new(:message => error.message)
27
+ when Errno::ECONNREFUSED
28
+ PuppetRepl::Exception::ConnectError.new(:message => error.message)
29
+ when Puppet::Error
30
+ if error.message =~ /could\ not\ find\ class/i
31
+ PuppetRepl::Exception::NoClassError.new(:default_modules_paths => default_modules_paths,
32
+ :message => error.message)
33
+ elsif error.message =~ /default\ node/i
34
+ PuppetRepl::Exception::NodeDefinitionError.new(:default_site_manifest => default_site_manifest,
35
+ :message => error.message)
36
+ else
37
+ error
38
+ end
39
+ else
40
+ error
41
+ end
42
+ end
43
+
19
44
  # returns an array of module directories, generally this is the only place
20
45
  # to look for puppet code by default. This is read from the puppet configuration
21
46
  def default_modules_paths
@@ -92,5 +117,9 @@ module PuppetRepl
92
117
  File.join(Puppet[:environmentpath],default_puppet_env_name,'manifests')
93
118
  end
94
119
 
120
+ def default_site_manifest
121
+ File.join(default_manifests_dir, 'site.pp')
122
+ end
123
+
95
124
  end
96
125
  end
data/lib/puppet-repl.rb CHANGED
@@ -3,6 +3,8 @@ require_relative 'version'
3
3
  require 'awesome_print'
4
4
  require_relative 'awesome_print/ext/awesome_puppet'
5
5
  require_relative 'trollop'
6
+ require 'puppet/util/log'
7
+ require_relative 'puppet-repl/support/errors'
6
8
  # monkey patch in some color effects string methods
7
9
  class String
8
10
  def red; "\033[31m#{self}\033[0m" end
@@ -18,3 +20,35 @@ class String
18
20
  split('_').map(&:capitalize).join
19
21
  end
20
22
  end
23
+
24
+ Puppet::Util::Log.newdesttype :buffer do
25
+ require 'puppet/util/colors'
26
+ include Puppet::Util::Colors
27
+
28
+ attr_accessor :err_buffer, :out_buffer
29
+
30
+ def initialize(err=$stderr, out=$stdout)
31
+ @err_buffer = err
32
+ @out_buffer = out
33
+ end
34
+
35
+ def handle(msg)
36
+ levels = {
37
+ :emerg => { :name => 'Emergency', :color => :hred, :stream => err_buffer },
38
+ :alert => { :name => 'Alert', :color => :hred, :stream => err_buffer },
39
+ :crit => { :name => 'Critical', :color => :hred, :stream => err_buffer },
40
+ :err => { :name => 'Error', :color => :hred, :stream => err_buffer },
41
+ :warning => { :name => 'Warning', :color => :hred, :stream => err_buffer },
42
+
43
+ :notice => { :name => 'Notice', :color => :reset, :stream => out_buffer },
44
+ :info => { :name => 'Info', :color => :green, :stream => out_buffer },
45
+ :debug => { :name => 'Debug', :color => :cyan, :stream => out_buffer },
46
+ }
47
+
48
+ str = msg.respond_to?(:multiline) ? msg.multiline : msg.to_s
49
+ str = msg.source == "Puppet" ? str : "#{msg.source}: #{str}"
50
+
51
+ level = levels[msg.level]
52
+ level[:stream].puts colorize(level[:color], "#{level[:name]}: #{str}")
53
+ end
54
+ end
data/lib/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module PuppetRepl
2
- VERSION = '0.1.1'
2
+ VERSION = '0.2.0'
3
3
  end
data/puppet-repl.gemspec CHANGED
@@ -2,18 +2,21 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
+ # stub: puppet-repl 0.2.0 ruby lib
5
6
 
6
7
  Gem::Specification.new do |s|
7
8
  s.name = "puppet-repl"
8
- s.version = "0.1.1"
9
+ s.version = "0.2.0"
9
10
 
10
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
+ s.require_paths = ["lib"]
11
13
  s.authors = ["Corey Osman"]
12
- s.date = "2016-04-17"
14
+ s.date = "2016-05-17"
13
15
  s.description = "A interactive command line tool for evaluating the puppet language"
14
16
  s.email = "corey@nwops.io"
15
17
  s.executables = ["prepl"]
16
18
  s.extra_rdoc_files = [
19
+ "CHANGELOG.md",
17
20
  "LICENSE.txt",
18
21
  "README.md"
19
22
  ]
@@ -35,6 +38,7 @@ Gem::Specification.new do |s|
35
38
  "lib/puppet-repl/support.rb",
36
39
  "lib/puppet-repl/support/compiler.rb",
37
40
  "lib/puppet-repl/support/environment.rb",
41
+ "lib/puppet-repl/support/errors.rb",
38
42
  "lib/puppet-repl/support/facts.rb",
39
43
  "lib/puppet-repl/support/functions.rb",
40
44
  "lib/puppet-repl/support/input_responders.rb",
@@ -44,7 +48,17 @@ Gem::Specification.new do |s|
44
48
  "lib/trollop.rb",
45
49
  "lib/version.rb",
46
50
  "puppet-repl.gemspec",
51
+ "resources/classes.png",
52
+ "resources/classification.png",
53
+ "resources/command_line_node.png",
54
+ "resources/functions.png",
55
+ "resources/hiera.png",
56
+ "resources/set_node.png",
57
+ "resources/tab_complete.png",
58
+ "resources/variables.png",
47
59
  "spec/fixtures/environments/production/manifests/site.pp",
60
+ "spec/fixtures/invalid_node_obj.yaml",
61
+ "spec/fixtures/node_obj.yaml",
48
62
  "spec/fixtures/sample_manifest.pp",
49
63
  "spec/prepl_spec.rb",
50
64
  "spec/puppet-repl_spec.rb",
@@ -53,8 +67,7 @@ Gem::Specification.new do |s|
53
67
  ]
54
68
  s.homepage = "http://github.com/nwops/puppet-repl"
55
69
  s.licenses = ["MIT"]
56
- s.require_paths = ["lib"]
57
- s.rubygems_version = "2.0.14"
70
+ s.rubygems_version = "2.4.5.1"
58
71
  s.summary = "A repl for the puppet language"
59
72
 
60
73
  if s.respond_to? :specification_version then
@@ -62,22 +75,19 @@ Gem::Specification.new do |s|
62
75
 
63
76
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
64
77
  s.add_runtime_dependency(%q<puppet>, [">= 3.8"])
65
- s.add_runtime_dependency(%q<facterdb>, [">= 0"])
66
- s.add_runtime_dependency(%q<awesome_print>, [">= 0"])
67
- s.add_development_dependency(%q<pry>, [">= 0"])
78
+ s.add_runtime_dependency(%q<facterdb>, ["~> 0.3"])
79
+ s.add_runtime_dependency(%q<awesome_print>, ["~> 1.6"])
68
80
  s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
69
81
  else
70
82
  s.add_dependency(%q<puppet>, [">= 3.8"])
71
- s.add_dependency(%q<facterdb>, [">= 0"])
72
- s.add_dependency(%q<awesome_print>, [">= 0"])
73
- s.add_dependency(%q<pry>, [">= 0"])
83
+ s.add_dependency(%q<facterdb>, ["~> 0.3"])
84
+ s.add_dependency(%q<awesome_print>, ["~> 1.6"])
74
85
  s.add_dependency(%q<rdoc>, ["~> 3.12"])
75
86
  end
76
87
  else
77
88
  s.add_dependency(%q<puppet>, [">= 3.8"])
78
- s.add_dependency(%q<facterdb>, [">= 0"])
79
- s.add_dependency(%q<awesome_print>, [">= 0"])
80
- s.add_dependency(%q<pry>, [">= 0"])
89
+ s.add_dependency(%q<facterdb>, ["~> 0.3"])
90
+ s.add_dependency(%q<awesome_print>, ["~> 1.6"])
81
91
  s.add_dependency(%q<rdoc>, ["~> 3.12"])
82
92
  end
83
93
  end
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,8 @@
1
+ --- !ruby/object:Puppet::Node
2
+ name: invalid
3
+ classes:
4
+ parameters:
5
+ facts:
6
+ time: 2016-05-10 16:28:21.502653 -07:00
7
+ environment_name: production
8
+ expiration: 2016-05-10 16:58:21.502660 -07:00