puppet-repl 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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