puppet-herald 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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,13 +1,15 @@
1
+ # Migration
1
2
  class CreateLogEntries < ActiveRecord::Migration
3
+ # Migration
2
4
  def change
3
5
  create_table :log_entries do |t|
4
- t.datetime :time
5
- t.string :level
6
- t.string :source
7
- t.integer :line
8
- t.text :message
9
-
10
- t.references :report
6
+ t.datetime :time
7
+ t.string :level
8
+ t.string :source
9
+ t.integer :line
10
+ t.text :message
11
+
12
+ t.references :report
11
13
  end
12
14
  end
13
15
  end
data/db/schema.rb CHANGED
@@ -11,37 +11,35 @@
11
11
  #
12
12
  # It's strongly recommended that you check this file into your version control system.
13
13
 
14
- ActiveRecord::Schema.define(version: 20141211171326) do
15
-
14
+ ActiveRecord::Schema.define(version: 20_141_211_171_326) do
16
15
  # These are extensions that must be enabled in order to support this database
17
- enable_extension "plpgsql"
16
+ enable_extension 'plpgsql'
18
17
 
19
- create_table "log_entries", force: true do |t|
20
- t.datetime "time"
21
- t.string "level"
22
- t.string "source"
23
- t.integer "line"
24
- t.text "message"
25
- t.integer "report_id"
18
+ create_table 'log_entries', force: true do |t|
19
+ t.datetime 'time'
20
+ t.string 'level'
21
+ t.string 'source'
22
+ t.integer 'line'
23
+ t.text 'message'
24
+ t.integer 'report_id'
26
25
  end
27
26
 
28
- create_table "nodes", force: true do |t|
29
- t.string "name"
30
- t.string "status"
31
- t.integer "no_of_reports"
32
- t.datetime "last_run"
27
+ create_table 'nodes', force: true do |t|
28
+ t.string 'name'
29
+ t.string 'status'
30
+ t.integer 'no_of_reports'
31
+ t.datetime 'last_run'
33
32
  end
34
33
 
35
- create_table "reports", force: true do |t|
36
- t.string "status"
37
- t.string "environment"
38
- t.string "transaction_uuid"
39
- t.string "configuration_version"
40
- t.string "puppet_version"
41
- t.string "kind"
42
- t.string "host"
43
- t.datetime "time"
44
- t.integer "node_id"
34
+ create_table 'reports', force: true do |t|
35
+ t.string 'status'
36
+ t.string 'environment'
37
+ t.string 'transaction_uuid'
38
+ t.string 'configuration_version'
39
+ t.string 'puppet_version'
40
+ t.string 'kind'
41
+ t.string 'host'
42
+ t.datetime 'time'
43
+ t.integer 'node_id'
45
44
  end
46
-
47
45
  end
data/lib/puppet-herald.rb CHANGED
@@ -1,49 +1,87 @@
1
1
  begin
2
2
  require 'pry'
3
- rescue LoadError
3
+ rescue LoadError # rubocop:disable Lint/HandleExceptions
4
4
  # Do nothing here
5
5
  end
6
6
 
7
+ require 'puppet-herald/database'
8
+
9
+ # A module for Herald
7
10
  module PuppetHerald
11
+ @root = File.dirname(File.dirname(File.realpath(__FILE__)))
12
+ @database = PuppetHerald::Database.new
8
13
 
9
- @@root = File.dirname(File.dirname(File.realpath(__FILE__)))
14
+ # A database object
15
+ # @return [PuppetHerald::Database] a database object
16
+ def self.database # rubocop:disable Style/TrivialAccessors
17
+ @database
18
+ end
10
19
 
11
- def self.relative_dir dir
12
- File.realpath(File.join @@root, dir)
20
+ # Calculates a replative directory inside the project
21
+ #
22
+ # @param dir [String] a sub directory
23
+ # @return [String] a full path to replative dir
24
+ def self.relative_dir(dir)
25
+ File.realpath(File.join @root, dir)
13
26
  end
14
27
 
28
+ def self.environment=(environment)
29
+ rackenv = :production
30
+ envsymbol = environment.to_s.to_sym
31
+ case envsymbol
32
+ when :dev, :development
33
+ rackenv = :development
34
+ when :test, :ci
35
+ rackenv = :test
36
+ else
37
+ rackenv = :production
38
+ envsymbol = :production
39
+ end
40
+ ENV['PUPPET_HERALD_ENV'] = envsymbol.to_s
41
+ ENV['RACK_ENV'] = rackenv.to_s
42
+ end
43
+
44
+ # Gets the environment set for Herald
45
+ # @return [Symbol] an environment
15
46
  def self.environment
16
47
  env = :production
17
- unless ENV['PUPPET_HERALD_ENV'].nil?
18
- env = ENV['PUPPET_HERALD_ENV'].to_sym
19
- end
20
- return env
48
+ env = ENV['PUPPET_HERALD_ENV'].to_sym unless ENV['PUPPET_HERALD_ENV'].nil?
49
+ ENV['RACK_ENV'] = env.to_s
50
+ env
21
51
  end
22
52
 
23
- def self.is_in_dev?
24
- return [:development, :dev, :test, :ci].include? environment
53
+ # Checks is running in DEVELOPMENT kind of environment (dev, ci, test)
54
+ #
55
+ # @return [Boolean] true if runs in development
56
+ def self.in_dev?
57
+ [:development, :dev, :test, :ci].include? environment
25
58
  end
26
59
 
27
- def self.is_in_prod?
28
- return !is_in_dev?
60
+ # Checks is running in production environment
61
+ #
62
+ # @return [Boolean] true if runs in production
63
+ def self.in_prod?
64
+ !in_dev?
29
65
  end
30
66
 
31
- def self.bug ex
67
+ # Reports a bug in desired format
68
+ #
69
+ # @param ex [Exception] an exception that was thrown
70
+ # @return [Hash] a hash with info about bug to be displayed to user
71
+ def self.bug(ex)
32
72
  file = Tempfile.new(['puppet-herald-bug', '.log'])
33
73
  filepath = file.path
34
74
  file.close
35
75
  file.unlink
36
- message = "v#{PuppetHerald::VERSION}-#{ex.class.to_s}: #{ex.message}"
76
+ message = "v#{PuppetHerald::VERSION}-#{ex.class}: #{ex.message}"
37
77
  contents = message + "\n\n" + ex.backtrace.join("\n") + "\n"
38
78
  File.write(filepath, contents)
39
79
  bugo = {
40
- :message => message,
41
- :homepage => PuppetHerald::HOMEPAGE,
42
- :bugfile => filepath,
43
- :help => "Please report this bug to #{PuppetHerald::HOMEPAGE} by passing contents of bug file: #{filepath}"
80
+ message: message,
81
+ homepage: PuppetHerald::HOMEPAGE,
82
+ bugfile: filepath,
83
+ help: "Please report this bug to #{PuppetHerald::HOMEPAGE} by passing contents of bug file: #{filepath}"
44
84
  }
45
- return bugo
85
+ bugo
46
86
  end
47
87
  end
48
-
49
- require 'puppet-herald/database'
@@ -0,0 +1,111 @@
1
+ require 'sinatra/base'
2
+ require 'sinatra/namespace'
3
+ require 'puppet-herald'
4
+ require 'puppet-herald/app/configuration'
5
+ require 'puppet-herald/models/node'
6
+ require 'puppet-herald/models/report'
7
+
8
+ # A module for Herald
9
+ module PuppetHerald
10
+ # Module that holds modules of Sinatra app
11
+ module App
12
+ # An implementation of API v1
13
+ class ApiImplV1
14
+ # Constructor
15
+ # @param app [Sinatra::Base] an app module
16
+ # @return [ApiImplV1] an API impl
17
+ def initialize(app)
18
+ @app = app
19
+ end
20
+ # Creates a new report
21
+ # @param request [Sinatra::Request] an request object
22
+ # @return [Array] an response array: [code, body] or [code, headers, body]
23
+ def post_reports(request)
24
+ yaml = request.body.read
25
+ report = PuppetHerald::Models::Report.create_from_yaml yaml
26
+ body = { id: report.id }.to_json
27
+ [201, body]
28
+ end
29
+ # Get a report by its ID
30
+ # @param params [Hash] an requests parsed params
31
+ # @return [Array] an response array: [code, body] or [code, headers, body]
32
+ def get_report(params)
33
+ id = params[:id]
34
+ report = PuppetHerald::Models::Report.get_with_log_entries(id)
35
+ status = 200
36
+ status = 404 if report.nil?
37
+ body = report.to_json(include: :log_entries)
38
+ [status, body]
39
+ end
40
+ # Gets all nodes
41
+ # @return [Array] an response array: [code, body] or [code, headers, body]
42
+ def nodes
43
+ nodes = PuppetHerald::Models::Node.all
44
+ [200, nodes.to_json]
45
+ end
46
+ # Gets a node by its ID
47
+ # @param params [Hash] an requests parsed params
48
+ # @return [Array] an response array: [code, body] or [code, headers, body]
49
+ def get_node(params)
50
+ id = params[:id]
51
+ node = PuppetHerald::Models::Node.get_with_reports(id)
52
+ status = 200
53
+ status = 404 if node.nil?
54
+ body = node.to_json(include: :reports)
55
+ [status, body]
56
+ end
57
+ # Get a app's artifact information
58
+ # @return [Array] an response array: [code, body] or [code, headers, body]
59
+ def version_json
60
+ ver = {}
61
+ [:VERSION, :LICENSE, :NAME, :PACKAGE, :SUMMARY, :DESCRIPTION, :HOMEPAGE].each do |const|
62
+ ver[const.downcase] = PuppetHerald.const_get const
63
+ end
64
+ [200, ver.to_json]
65
+ end
66
+ end
67
+
68
+ # An API app module
69
+ class Api < Sinatra::Base
70
+ register Sinatra::Namespace
71
+ use PuppetHerald::App::Configuration
72
+ set :api, ApiImplV1.new(self)
73
+ helpers do
74
+ # API getter
75
+ # @return [ApiImplV1] an api object
76
+ def api
77
+ settings.api
78
+ end
79
+ end
80
+
81
+ get '/version.json' do
82
+ content_type 'application/json'
83
+ api.version_json
84
+ end
85
+
86
+ namespace '/api' do
87
+ namespace '/v1' do
88
+ post '/reports' do
89
+ content_type 'application/json'
90
+ api.post_reports request
91
+ end
92
+
93
+ get '/nodes' do
94
+ content_type 'application/json'
95
+ api.nodes
96
+ end
97
+
98
+ get '/nodes/:id' do
99
+ content_type 'application/json'
100
+ api.get_node params
101
+ end
102
+
103
+ get '/reports/:id' do
104
+ content_type 'application/json'
105
+ api.get_report params
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,70 @@
1
+ require 'logger'
2
+ require 'sinatra/base'
3
+ require 'sinatra/namespace'
4
+ require 'sinatra/activerecord'
5
+ require 'puppet-herald'
6
+ require 'puppet-herald/javascript'
7
+
8
+ # A module for Herald
9
+ module PuppetHerald
10
+ # A module that holds module of app
11
+ module App
12
+ # A configuration for application
13
+ class Configuration < Sinatra::Base
14
+ register Sinatra::ActiveRecordExtension
15
+
16
+ set :database, PuppetHerald.database.spec unless PuppetHerald.database.spec.nil?
17
+
18
+ class << self
19
+ # Migrates a database to state desired for the application
20
+ #
21
+ # @return [nil]
22
+ def dbmigrate!
23
+ ActiveRecord::Base.establish_connection(PuppetHerald.database.spec)
24
+ setup_database_logger
25
+ ActiveRecord::Migrator.up 'db/migrate'
26
+ ActiveRecord::Base.clear_active_connections!
27
+ nil
28
+ end
29
+
30
+ # Is request part of the API
31
+ # @param req [Sinatra::Request] a request to check
32
+ # @return [Boolean] true, if given request point to part of the API
33
+ def api?(req)
34
+ (req.path.start_with?('/api') || req.path.start_with?('/version.json'))
35
+ end
36
+
37
+ private
38
+
39
+ # Sets logger level for database handlers
40
+ #
41
+ # @return [nil]
42
+ def setup_database_logger
43
+ if PuppetHerald.in_dev?
44
+ ActiveRecord::Base.logger.level = Logger::DEBUG
45
+ else
46
+ ActiveRecord::Base.logger.level = Logger::WARN
47
+ end
48
+ nil
49
+ end
50
+ end
51
+
52
+ if PuppetHerald.in_dev?
53
+ set :environment, :development
54
+ else
55
+ set :environment, :production
56
+ end
57
+ set :show_exceptions, false
58
+
59
+ error do
60
+ @bug = PuppetHerald.bug env['sinatra.error']
61
+ if PuppetHerald::App::Configuration.api? request
62
+ content_type 'application/json'
63
+ @bug.to_json
64
+ else
65
+ erb :err500
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,61 @@
1
+ require 'puppet-herald'
2
+ require 'puppet-herald/javascript'
3
+ require 'sinatra/base'
4
+ require 'sinatra/namespace'
5
+
6
+ # A module for Herald
7
+ module PuppetHerald
8
+ # A module that holds module of app
9
+ module App
10
+ # Frontend logic impl internal class
11
+ class LogicImpl
12
+ def initialize
13
+ @js = PuppetHerald::Javascript.new
14
+ end
15
+ # Gets an app.html
16
+ # @dodgy executed also to raise an exception for testing (application_spec)
17
+ def app_html
18
+ if PuppetHerald.in_prod?
19
+ minified = '.min'
20
+ files = ['/app.min.js']
21
+ else
22
+ minified = ''
23
+ files = @js.files
24
+ end
25
+ [minified, files]
26
+ end
27
+ # Uglify an application JS's into one minified JS file
28
+ # @param mapname [String] name of source map to be put into uglified JS
29
+ # @return [Hash] a hash with uglified JS and source map
30
+ def uglify(mapname)
31
+ @js.uglify mapname
32
+ end
33
+ end
34
+ # Class that holds implementation of frontent interface
35
+ class Frontend < Sinatra::Base
36
+ use PuppetHerald::App::Configuration
37
+ set :logic, PuppetHerald::App::LogicImpl.new
38
+
39
+ get '/' do
40
+ redirect '/app.html', 301
41
+ end
42
+
43
+ get '/index.html' do
44
+ redirect '/app.html', 301
45
+ end
46
+
47
+ get '/app.html' do
48
+ cache_control :public, :must_revalidate, max_age: 60 if PuppetHerald.in_prod?
49
+ @minified, @files = settings.logic.app_html
50
+ erb :app
51
+ end
52
+
53
+ get(/\/app\.min\.(js\.map|js)/) do |ext|
54
+ content_type 'application/javascript'
55
+ contents = settings.logic.uglify('/app.min.js.map')[ext]
56
+ cache_control :public, :must_revalidate, max_age: 60 if PuppetHerald.in_prod?
57
+ contents
58
+ end
59
+ end
60
+ end
61
+ end
@@ -1,9 +1,6 @@
1
1
  <!DOCTYPE html>
2
- <!--[if lt IE 7]> <html lang="en" ng-app="herald" class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
3
- <!--[if IE 7]> <html lang="en" ng-app="herald" class="no-js lt-ie9 lt-ie8"> <![endif]-->
4
- <!--[if IE 8]> <html lang="en" ng-app="herald" class="no-js lt-ie9"> <![endif]-->
5
- <!--[if gt IE 8]><!--> <html lang="en" ng-app="herald" class="no-js"> <!--<![endif]-->
6
- <head ng-controller="AppController as app">
2
+ <html lang="en" ng-app="herald" ng-controller="AppController">
3
+ <head>
7
4
  <meta charset="utf-8">
8
5
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
9
6
  <title>Herald - a Puppet report processor</title>
@@ -20,7 +17,7 @@
20
17
 
21
18
  <section class="container-fluid">
22
19
 
23
- <h2><img src="/img/shield97.svg" />Herald <small>a Puppet report processor</small></h2>
20
+ <h2><a href="#/"><img src="/img/shield97.svg" />Herald</a> <small>a Puppet report processor</small></h2>
24
21
 
25
22
  <div ng-view></div>
26
23
 
@@ -32,8 +29,8 @@
32
29
 
33
30
  <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.6/angular<%= @minified %>.js"></script>
34
31
  <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.6/angular-route<%= @minified %>.js"></script>
35
- <script src="//cdn.jsdelivr.net/momentjs/2.5.1/moment.min.js"></script>
36
- <script src="//cdn.jsdelivr.net/angular.moment/0.6.2/angular-moment.js"></script>
32
+ <script src="//cdn.jsdelivr.net/momentjs/2.8.4/moment.min.js"></script>
33
+ <script src="//cdn.jsdelivr.net/angular.moment/0.8.3/angular-moment<%= @minified %>.js"></script>
37
34
 
38
35
  <!-- Automatic incudes of project files -->
39
36
  <% @files.each do |file| %>