puppet-herald 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.
Files changed (65) hide show
  1. checksums.yaml +15 -7
  2. data/.rubocop.yml +31 -0
  3. data/.rubocop_todo.yml +6 -0
  4. data/.travis.yml +7 -7
  5. data/Gemfile +5 -9
  6. data/README.md +152 -16
  7. data/Rakefile +67 -6
  8. data/bin/puppet-herald +1 -1
  9. data/config.ru +2 -2
  10. data/db/migrate/20141211165540_create_nodes.rb +5 -3
  11. data/db/migrate/20141211171305_create_reports.rb +12 -10
  12. data/db/migrate/20141211171326_create_log_entries.rb +9 -7
  13. data/db/schema.rb +24 -26
  14. data/lib/puppet-herald.rb +59 -21
  15. data/lib/puppet-herald/app/api.rb +111 -0
  16. data/lib/puppet-herald/app/configuration.rb +70 -0
  17. data/lib/puppet-herald/app/frontend.rb +61 -0
  18. data/lib/puppet-herald/{views → app/views}/app.erb +5 -8
  19. data/lib/puppet-herald/{views → app/views}/err500.erb +1 -4
  20. data/lib/puppet-herald/application.rb +27 -0
  21. data/lib/puppet-herald/cli.rb +66 -45
  22. data/lib/puppet-herald/client.rb +33 -0
  23. data/lib/puppet-herald/database.rb +84 -40
  24. data/lib/puppet-herald/javascript.rb +23 -17
  25. data/lib/puppet-herald/models/log-entry.rb +10 -3
  26. data/lib/puppet-herald/models/node.rb +15 -5
  27. data/lib/puppet-herald/models/report.rb +70 -63
  28. data/lib/puppet-herald/public/app.js +9 -8
  29. data/lib/puppet-herald/public/components/directives/status-button.html +1 -1
  30. data/lib/puppet-herald/public/components/directives/status-button.js +5 -3
  31. data/lib/puppet-herald/public/components/filters/filters.js +9 -4
  32. data/lib/puppet-herald/public/components/page.js +34 -0
  33. data/lib/puppet-herald/public/node/node.html +3 -1
  34. data/lib/puppet-herald/public/node/node.js +7 -4
  35. data/lib/puppet-herald/public/nodes/nodes.js +3 -2
  36. data/lib/puppet-herald/public/report/report.html +4 -1
  37. data/lib/puppet-herald/public/report/report.js +5 -3
  38. data/lib/puppet-herald/stubs/puppet.rb +20 -9
  39. data/lib/puppet-herald/version.rb +17 -7
  40. data/package.json +8 -3
  41. data/puppet-herald.gemspec +3 -6
  42. data/spec/integration/application_spec.rb +175 -0
  43. data/spec/integration/models/node_spec.rb +4 -4
  44. data/spec/integration/models/report_spec.rb +7 -7
  45. data/spec/spec_helper.rb +12 -7
  46. data/spec/support/active_record.rb +6 -10
  47. data/spec/support/reconnectdb.rb +13 -0
  48. data/spec/unit/puppet-herald/cli_spec.rb +45 -13
  49. data/spec/unit/puppet-herald/client_spec.rb +23 -0
  50. data/spec/unit/puppet-herald/database_spec.rb +8 -9
  51. data/spec/unit/puppet-herald/javascript_spec.rb +8 -13
  52. data/spec/unit/puppet-herald_spec.rb +4 -4
  53. data/test/javascript/karma.conf.js +43 -5
  54. data/test/javascript/src/app_test.js +90 -0
  55. data/test/javascript/src/components/artifact/artifact-directive_test.js +36 -0
  56. data/test/javascript/src/components/artifact/artifact_test.js +64 -0
  57. data/test/javascript/src/components/directives/status-button_test.js +159 -0
  58. data/test/javascript/src/components/filters/filters_test.js +35 -0
  59. data/test/javascript/src/node/node_test.js +87 -0
  60. data/test/javascript/src/nodes/nodes_test.js +56 -0
  61. data/test/javascript/src/report/report_test.js +94 -0
  62. metadata +98 -68
  63. data/lib/puppet-herald/app.rb +0 -103
  64. data/lib/puppet-herald/public/components/artifact/artifact-directive_test.js +0 -17
  65. data/spec/integration/app_spec.rb +0 -21
@@ -1,8 +1,5 @@
1
1
  <!DOCTYPE html>
2
- <!--[if lt IE 7]> <html lang="en" class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
3
- <!--[if IE 7]> <html lang="en" class="no-js lt-ie9 lt-ie8"> <![endif]-->
4
- <!--[if IE 8]> <html lang="en" class="no-js lt-ie9"> <![endif]-->
5
- <!--[if gt IE 8]><!--> <html lang="en" class="no-js"> <!--<![endif]-->
2
+ <html lang="en">
6
3
  <head>
7
4
  <meta charset="utf-8">
8
5
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
@@ -0,0 +1,27 @@
1
+ require 'sinatra/base'
2
+
3
+ require 'puppet-herald/app/configuration'
4
+ require 'puppet-herald/app/api'
5
+ require 'puppet-herald/app/frontend'
6
+
7
+ # A module for Herald
8
+ module PuppetHerald
9
+ # Class for an Herald sinatra application
10
+ class Application < Sinatra::Application
11
+ use PuppetHerald::App::Configuration
12
+ use PuppetHerald::App::Frontend
13
+ use PuppetHerald::App::Api
14
+
15
+ class << self
16
+ # Executes the Herald application
17
+ #
18
+ # @param options [Hash] an extra options for Rack server
19
+ # @param block [block] an extra configuration block
20
+ # @return [Sinatra::Application] an Herald application
21
+ def run!(options = {}, &block)
22
+ PuppetHerald::App::Configuration.dbmigrate!
23
+ super options, *block
24
+ end
25
+ end
26
+ end
27
+ end
@@ -4,77 +4,98 @@ require 'puppet-herald'
4
4
  require 'puppet-herald/version'
5
5
  require 'puppet-herald/database'
6
6
 
7
+ # A module for Herald
7
8
  module PuppetHerald
9
+ # A CLI class
8
10
  class CLI
11
+ # Initialize CLI
12
+ # @return [CLI] an CLI object
13
+ def initialize
14
+ @logger = Logger.new STDOUT
15
+ @errlogger = Logger.new STDERR
16
+ self
17
+ end
18
+
19
+ # Logger for CLI interface (error and std)
20
+ # @return [Logger] logger for CLI
21
+ attr_reader :logger, :errlogger
9
22
 
10
- @@logger = Logger.new STDOUT
11
- @@errlogger = Logger.new STDERR
12
- @@retcode = 0
23
+ # Executes an Herald app from CLI
24
+ #
25
+ # @param argv [Array] an argv from CLI
26
+ # @return [Integer] a status code for program
27
+ def run!(argv = ARGV)
28
+ PuppetHerald.environment
13
29
 
14
- def self.retcode= val
15
- @@retcode = val if @@retcode == 0
30
+ options = parse_or_kill argv, 2
31
+ run_or_kill options, 1
32
+ Kernel.exit 0
16
33
  end
17
34
 
18
- def self.logger
19
- @@logger
35
+ protected
36
+
37
+ # Parse an ARGV command line arguments
38
+ # @param argv [Array] an argv from CLI
39
+ # @return [Hash] options to use by application
40
+ def parse(argv)
41
+ options = parser.process!(argv)
42
+
43
+ logger.info "Starting #{PuppetHerald::NAME} v#{PuppetHerald::VERSION} in #{PuppetHerald.environment}..."
44
+ PuppetHerald.database.dbconn = options[:dbconn]
45
+ PuppetHerald.database.passfile = options[:passfile]
46
+ PuppetHerald.database.spec(true)
47
+ options
20
48
  end
21
49
 
22
- def self.errlogger
23
- @@errlogger
50
+ private
51
+
52
+ def run_or_kill(options, retcode)
53
+ require 'puppet-herald/application'
54
+ PuppetHerald::Application.run! options
55
+ rescue StandardError => ex
56
+ bug = PuppetHerald.bug(ex)
57
+ errlogger.fatal "Unexpected error occured, mayby a bug?\n\n#{bug[:message]}\n\n#{bug[:help]}"
58
+ Kernel.exit retcode
24
59
  end
25
60
 
26
- def self.run! argv=ARGV
27
- @@retcode = 0
28
- begin
29
- options = parse_options argv
30
- require 'puppet-herald/app'
31
- PuppetHerald::App.run! options
32
- rescue Exception => ex
33
- bug = PuppetHerald.bug(ex)
34
- errlogger.fatal "Unexpected error occured, mayby a bug?\n\n#{bug[:message]}\n\n#{bug[:help]}"
35
- self.retcode = 1
36
- end
37
- Kernel::exit @@retcode
61
+ def parse_or_kill(argv, retcode)
62
+ return parse argv
63
+ rescue StandardError => ex
64
+ errlogger.fatal "Database configuration is invalid!\n\n#{ex.message}"
65
+ Kernel.exit retcode
38
66
  end
39
67
 
40
- def self.parse_options argv
41
- usage = ""
42
- banner = <<-eos
68
+ def banner
69
+ txt = <<-eos
43
70
  #{PuppetHerald::NAME} v#{PuppetHerald::VERSION} - #{PuppetHerald::SUMMARY}
44
71
 
45
72
  #{PuppetHerald::DESCRIPTION}
46
73
 
47
- Usage: #{$0} [options]
74
+ Usage: #{$PROGRAM_NAME} [options]
48
75
 
49
76
  For --dbconn option you can use both PostgreSQL and SQLite3 (postgresql://host/db, sqlite://file/path).
50
77
  CAUTION! For security reasons, don't pass password in connection string, use --passfile option!
51
78
 
52
79
  eos
80
+ txt
81
+ end
82
+
83
+ def parser
53
84
  home = File.expand_path('~')
54
85
  defaultdb = "sqlite://#{home}/pherald.db"
55
86
  defaultdbpass = "#{home}/.pherald.pass"
56
- parser = Parser.new do |p|
87
+ Parser.new do |p|
57
88
  p.banner = banner
58
89
  p.version = PuppetHerald::VERSION
59
- p.option :bind, "Hostname to bind to", :default => 'localhost'
60
- p.option :port, "Port to use", :default => 11303, :value_satisfies => lambda {|x| x >= 100 && x <= 65000}
61
- p.option :dbconn, "Connection string to database, see info above", :default => defaultdb
62
- p.option :passfile, "If using postgresql, this file will be read for password to database", :default => defaultdbpass
63
- end
64
- options = parser.process!(argv)
65
-
66
- logger.info "Starting #{PuppetHerald::NAME} v#{PuppetHerald::VERSION} in #{PuppetHerald::environment}..."
67
- PuppetHerald::Database.dbconn = options[:dbconn]
68
- PuppetHerald::Database.passfile = options[:passfile]
69
- begin
70
- PuppetHerald::Database.spec :echo => true
71
- rescue Exception => ex
72
- errlogger.fatal "Database configuration is invalid!\n\n#{ex.message}"
73
- self.retcode = 2
74
- raise ex
90
+ p.option :bind, 'Hostname to bind to', default: 'localhost'
91
+ p.option :port, 'Port to use', default: 11_303, value_satisfies: ->(x) { x >= 1 && x <= 65_535 }
92
+ p.option :dbconn, 'Connection string to database, see info above', default: defaultdb
93
+ p.option(
94
+ :passfile,
95
+ 'If using postgresql, this file will be read for password to database',
96
+ default: defaultdbpass
97
+ )
75
98
  end
76
-
77
- return options
78
99
  end
79
100
  end
80
- end
101
+ end
@@ -0,0 +1,33 @@
1
+ require 'net/http'
2
+
3
+ # A module for Herald
4
+ module PuppetHerald
5
+ # A client class for Herald
6
+ class Client
7
+ # Constructs a client
8
+ #
9
+ # @param host [String] a host to connect to, default to +'localhost'+
10
+ # @param port [Integer] a port to connect to, default to +11303+
11
+ # @return [PuppetHerald::Client] a client instance
12
+ def initialize(host = 'localhost', port = 11_303)
13
+ @host = host
14
+ @port = port
15
+ self
16
+ end
17
+
18
+ # Process a puppet report and sends it to Herald
19
+ #
20
+ # @param report [Puppet::Transaction::Report] a puppet report
21
+ # @param block [Proc] a optional block that can modify request before sending
22
+ # @return [Boolean] true if everything is ok
23
+ def process(report, &block)
24
+ path = '/api/v1/reports'
25
+ header = { 'Content-Type' => 'application/yaml' }
26
+ req = Net::HTTP::Post.new(path, initheader = header) # rubocop:disable all
27
+ req.body = report.to_yaml
28
+ block.call(req) if block
29
+ Net::HTTP.new(@host, @port).start { |http| http.request(req) }
30
+ true
31
+ end
32
+ end
33
+ end
@@ -1,57 +1,101 @@
1
1
  require 'fileutils'
2
2
  require 'logger'
3
3
 
4
+ # A module for Herald
4
5
  module PuppetHerald
6
+ # A class for a database configuration
5
7
  class Database
8
+ def initialize
9
+ @dbconn = nil
10
+ @passfile = nil
11
+ @logger = Logger.new STDOUT
12
+ end
13
+
14
+ # Gets a logger for database
15
+ # @return [Logger] a logger
16
+ attr_reader :logger
17
+
18
+ # Sets a database connection
19
+ # @return [String] a dbconnection string
20
+ attr_writer :dbconn
6
21
 
7
- @@dbconn = nil
8
- @@passfile = nil
9
- @@logger = Logger.new STDOUT
22
+ # Sets a passfile
23
+ # @return [String] a password file
24
+ attr_writer :passfile
10
25
 
11
- def self.logger
12
- @@logger
26
+ # Compiles a spec for database creation
27
+ #
28
+ # @param log [Boolean] should log on screen?
29
+ # @return [Hash] a database configuration
30
+ def spec(log = false)
31
+ connection = process_spec
32
+ print_config(connection, log)
33
+ connection
13
34
  end
14
35
 
15
- def self.dbconn= dbconn
16
- @@dbconn = dbconn
36
+ private
37
+
38
+ def process_spec
39
+ match = validate_conn(@dbconn)
40
+ if %w(sqlite sqlite3).include? match[1]
41
+ connection = sqlite(match)
42
+ else
43
+ connection = postgresql(@dbconn, @passfile)
44
+ end
45
+ connection
17
46
  end
18
47
 
19
- def self.passfile= passfile
20
- @@passfile = passfile
48
+ def validate_conn(dbconn)
49
+ fail 'Connection is not set, can not validate database connection' if dbconn.nil?
50
+ match = dbconn.match(%r{^(sqlite3?|postgres(?:ql)?)://(.+)$})
51
+
52
+ fail "Invalid database connection string given: #{dbconn}" if match.nil?
53
+ match
54
+ end
55
+
56
+ def print_config(connection, log)
57
+ return unless log
58
+ copy = connection.dup
59
+ copy[:password] = '***' unless copy[:password].nil?
60
+ logger.info "Using #{copy.inspect} for database."
21
61
  end
22
62
 
23
- def self.spec echo=false
24
- return nil if @@dbconn.nil?
63
+ def sqlite(match)
25
64
  connection = {}
26
- match = @@dbconn.match(/^(sqlite3?|postgres(?:ql)?):\/\/(.+)$/)
27
- unless match
28
- raise "Invalid database connection string given: #{@@dbconn}"
29
- end
30
- if ['sqlite', 'sqlite3'].include? match[1]
31
- dbname = match[2]
32
- unless dbname.match /^(?:file:)?:mem/
33
- dbname = File.expand_path(dbname)
34
- FileUtils.touch dbname
35
- end
36
- connection[:adapter] = 'sqlite3'
37
- connection[:database] = dbname
38
- else
39
- db = URI.parse @@dbconn
40
- dbname = db.path[1..-1]
41
- connection[:adapter] = db.scheme == 'postgres' ? 'postgresql' : db.scheme
42
- connection[:host] = db.host
43
- connection[:port] = db.port unless db.port.nil?
44
- connection[:username] = db.user.nil? ? dbname : db.user
45
- connection[:password] = File.read(@@passfile).strip
46
- connection[:database] = dbname
47
- connection[:encoding] = 'utf8'
65
+ dbname = match[2]
66
+ unless dbname.match(/^(?:file:)?:mem/)
67
+ dbname = File.expand_path(dbname)
68
+ FileUtils.touch dbname
48
69
  end
49
- if echo
50
- copy = connection.dup
51
- copy[:password] = '***' unless copy[:password].nil?
52
- logger.info "Using #{copy.inspect} for database."
53
- end
54
- return connection
70
+ connection[:adapter] = 'sqlite3'
71
+ connection[:database] = dbname
72
+ connection
73
+ end
74
+
75
+ def postgresql(conn, passfile)
76
+ connection = {}
77
+ db = URI.parse conn
78
+ dbname = db.path[1..-1]
79
+ connection[:adapter] = pgscheme(db.scheme)
80
+ connection[:host] = db.host
81
+ connection[:username] = pguser(db.user, dbname)
82
+ connection[:password] = File.read(passfile).strip
83
+ connection[:database] = dbname
84
+ connection[:encoding] = 'utf8'
85
+ pgport(connection, db.port)
86
+ connection
87
+ end
88
+
89
+ def pgport(connection, port)
90
+ connection[:port] = port unless port.nil?
91
+ end
92
+
93
+ def pgscheme(scheme)
94
+ scheme == 'postgres' ? 'postgresql' : scheme
95
+ end
96
+
97
+ def pguser(user, dbname)
98
+ user.nil? ? dbname : user
55
99
  end
56
100
  end
57
- end
101
+ end
@@ -1,34 +1,40 @@
1
1
  require 'puppet-herald'
2
2
  require 'uglifier'
3
3
 
4
+ # A module for Herald
4
5
  module PuppetHerald
6
+ # A javascript processing class
5
7
  class Javascript
8
+ # Initialize JS class
9
+ def initialize
10
+ @files = nil
11
+ @base = 'lib/puppet-herald/public'
12
+ end
6
13
 
7
- @@files = nil
8
-
9
- @@base = 'lib/puppet-herald/public'
10
-
11
- def self.files
12
- if PuppetHerald::is_in_dev?
13
- @@files = nil
14
- end
15
- if @@files.nil?
16
- public_dir = PuppetHerald::relative_dir(@@base)
14
+ # Returns a list of JS files to be inserted into main HTML
15
+ # @return [Array] list of JS's
16
+ def files
17
+ @files = nil if PuppetHerald.in_dev?
18
+ if @files.nil?
19
+ public_dir = PuppetHerald.relative_dir(@base)
17
20
  all = Dir.chdir(public_dir) { Dir.glob('**/*.js') }
18
- @@files = all.reverse.reject { |file| file.match(/_test\.js$/) }
21
+ @files = all.reverse.reject { |file| file.match(/_test\.js$/) }
19
22
  end
20
- return @@files
23
+ @files
21
24
  end
22
25
 
23
- def self.uglify mapname
24
- sources = files.collect { |file| File.read("#{@@base}/#{file}") }
26
+ # Uglify an application JS's into one minified JS file
27
+ # @param mapname [String] name of source map to be put into uglified JS
28
+ # @return [Hash] a hash with uglified JS and source map
29
+ def uglify(mapname)
30
+ sources = files.collect { |file| File.read("#{@base}/#{file}") }
25
31
  source = sources.join "\n"
26
- uglifier = Uglifier.new(:source_map_url => mapname)
32
+ uglifier = Uglifier.new(source_map_url: mapname)
27
33
  uglified, source_map = uglifier.compile_with_map(source)
28
- return {
34
+ {
29
35
  'js' => uglified,
30
36
  'js.map' => source_map
31
37
  }
32
38
  end
33
39
  end
34
- end
40
+ end
@@ -1,3 +1,10 @@
1
- class LogEntry < ActiveRecord::Base
2
- belongs_to :report
3
- end
1
+ # A module for Herald
2
+ module PuppetHerald
3
+ # module for models
4
+ module Models
5
+ # A log entry model
6
+ class LogEntry < ActiveRecord::Base
7
+ belongs_to :report
8
+ end
9
+ end
10
+ end
@@ -1,7 +1,17 @@
1
- class Node < ActiveRecord::Base
2
- has_many :reports, dependent: :delete_all
1
+ # A module for Herald
2
+ module PuppetHerald
3
+ # module for models
4
+ module Models
5
+ # A node model
6
+ class Node < ActiveRecord::Base
7
+ has_many :reports, dependent: :delete_all
3
8
 
4
- def self.get_with_reports id
5
- Node.where("id = ?", id).includes(:reports).first
9
+ # Gets a node with prefetched reports
10
+ # @param id [Integer] a in of node to get
11
+ # @return [Node, nil] fetched node or nil
12
+ def self.get_with_reports(id)
13
+ Node.joins(:reports).includes(:reports).find_by_id(id)
14
+ end
15
+ end
6
16
  end
7
- end
17
+ end