puppet-herald 0.2.0 → 0.8.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 (74) hide show
  1. checksums.yaml +5 -13
  2. data/.gitignore +3 -0
  3. data/.jshintrc +19 -0
  4. data/Gemfile +25 -12
  5. data/Gemfile.local.example +3 -0
  6. data/Guardfile +57 -0
  7. data/README.md +1 -1
  8. data/Rakefile +100 -20
  9. data/db/migrate/20141218200108_remove_no_of_reports_from_nodes.rb +7 -0
  10. data/lib/puppet-herald.rb +95 -65
  11. data/lib/puppet-herald/app/api.rb +79 -12
  12. data/lib/puppet-herald/app/configuration.rb +47 -15
  13. data/lib/puppet-herald/app/frontend.rb +6 -6
  14. data/lib/puppet-herald/app/views/app.erb +14 -20
  15. data/lib/puppet-herald/app/views/err500.erb +8 -5
  16. data/lib/puppet-herald/application.rb +9 -1
  17. data/lib/puppet-herald/cli.rb +5 -11
  18. data/lib/puppet-herald/database.rb +1 -7
  19. data/lib/puppet-herald/javascript.rb +16 -10
  20. data/lib/puppet-herald/models.rb +47 -0
  21. data/lib/puppet-herald/models/log-entry.rb +2 -0
  22. data/lib/puppet-herald/models/node.rb +49 -2
  23. data/lib/puppet-herald/models/report.rb +33 -9
  24. data/lib/puppet-herald/project.js +46 -0
  25. data/lib/puppet-herald/public/app.js +11 -9
  26. data/lib/puppet-herald/public/bower.json +10 -3
  27. data/lib/puppet-herald/public/components/artifact/artifact-directive.js +4 -0
  28. data/lib/puppet-herald/public/components/artifact/artifact.js +1 -3
  29. data/lib/puppet-herald/public/components/directives/directives.js +5 -1
  30. data/lib/puppet-herald/public/components/directives/status-button.html +1 -1
  31. data/lib/puppet-herald/public/components/directives/status-button.js +11 -11
  32. data/lib/puppet-herald/public/components/filters/filters.js +6 -2
  33. data/lib/puppet-herald/public/components/page.js +2 -2
  34. data/lib/puppet-herald/public/components/pagination.js +142 -0
  35. data/lib/puppet-herald/public/components/settings.js +25 -0
  36. data/lib/puppet-herald/public/css/herald.css +100 -3
  37. data/lib/puppet-herald/public/general/app.html +73 -0
  38. data/lib/puppet-herald/public/img/shield97-white.svg +53 -0
  39. data/lib/puppet-herald/public/img/shield97.png +0 -0
  40. data/lib/puppet-herald/public/node/node.html +27 -9
  41. data/lib/puppet-herald/public/node/node.js +43 -15
  42. data/lib/puppet-herald/public/nodes/nodes.html +25 -7
  43. data/lib/puppet-herald/public/nodes/nodes.js +29 -14
  44. data/lib/puppet-herald/public/report/report.html +60 -13
  45. data/lib/puppet-herald/public/report/report.js +21 -14
  46. data/lib/puppet-herald/public/router.js +55 -0
  47. data/lib/puppet-herald/purgecronjob.rb +35 -0
  48. data/lib/puppet-herald/version.rb +2 -2
  49. data/package.json +14 -16
  50. data/puppet-herald.gemspec +12 -7
  51. data/spec/integration/app/configuration_spec.rb +33 -0
  52. data/spec/integration/application_spec.rb +139 -20
  53. data/spec/integration/fixtures/nodes.yml +13 -0
  54. data/spec/integration/fixtures/pending-notify.yaml +346 -0
  55. data/spec/integration/fixtures/reports.yml +61 -0
  56. data/spec/integration/models/node_spec.rb +12 -3
  57. data/spec/integration/models/report_spec.rb +60 -4
  58. data/spec/spec_helper.rb +6 -10
  59. data/spec/support/active_record.rb +1 -0
  60. data/spec/support/fixtures.rb +16 -0
  61. data/spec/unit/puppet-herald/cli_spec.rb +4 -4
  62. data/spec/unit/puppet-herald/database_spec.rb +5 -3
  63. data/spec/unit/puppet-herald/purgecronjob_spec.rb +37 -0
  64. data/test/javascript/.bowerrc +3 -0
  65. data/test/javascript/bower.json +21 -0
  66. data/test/javascript/karma.conf.js +17 -22
  67. data/test/javascript/src/app_test.js +10 -61
  68. data/test/javascript/src/components/directives/status-button_test.js +10 -10
  69. data/test/javascript/src/components/paginate_test.js +183 -0
  70. data/test/javascript/src/node/node_test.js +16 -6
  71. data/test/javascript/src/nodes/nodes_test.js +14 -2
  72. data/test/javascript/src/report/report_test.js +6 -6
  73. data/test/javascript/src/router_test.js +79 -0
  74. metadata +642 -23
@@ -1,6 +1,3 @@
1
- require 'puppet-herald'
2
- require 'uglifier'
3
-
4
1
  # A module for Herald
5
2
  module PuppetHerald
6
3
  # A javascript processing class
@@ -14,11 +11,13 @@ module PuppetHerald
14
11
  # Returns a list of JS files to be inserted into main HTML
15
12
  # @return [Array] list of JS's
16
13
  def files
14
+ require 'puppet-herald'
17
15
  @files = nil if PuppetHerald.in_dev?
18
16
  if @files.nil?
19
17
  public_dir = PuppetHerald.relative_dir(@base)
20
18
  all = Dir.chdir(public_dir) { Dir.glob('**/*.js') }
21
- @files = all.reverse.reject { |file| file.match(/_test\.js$/) }
19
+ all = all.reverse.reject { |file| file.match(/_test\.js$/) }
20
+ @files = all.reject { |file| file.match(/bower_components/) }
22
21
  end
23
22
  @files
24
23
  end
@@ -27,14 +26,21 @@ module PuppetHerald
27
26
  # @param mapname [String] name of source map to be put into uglified JS
28
27
  # @return [Hash] a hash with uglified JS and source map
29
28
  def uglify(mapname)
30
- sources = files.collect { |file| File.read("#{@base}/#{file}") }
29
+ require 'uglifier'
30
+ filenames = files
31
+ sources = filenames.collect { |file| File.read("#{@base}/#{file}") }
31
32
  source = sources.join "\n"
32
- uglifier = Uglifier.new(source_map_url: mapname)
33
- uglified, source_map = uglifier.compile_with_map(source)
34
- {
35
- 'js' => uglified,
36
- 'js.map' => source_map
33
+ options = {
34
+ source_map_url: mapname,
35
+ source_filename: filenames[0],
36
+ compress: {
37
+ angular: true,
38
+ hoist_vars: true
39
+ }
37
40
  }
41
+ uglifier = Uglifier.new(options)
42
+ uglified, source_map = uglifier.compile_with_map(source)
43
+ { 'js' => uglified, 'js.map' => source_map }
38
44
  end
39
45
  end
40
46
  end
@@ -0,0 +1,47 @@
1
+ # A module for Herald
2
+ module PuppetHerald
3
+ # module for models
4
+ module Models
5
+ # Pagianation object
6
+ class Pagination
7
+ # Pagination headers
8
+ KEYS = {
9
+ page: 'X-Paginate-Page',
10
+ limit: 'X-Paginate-Limit',
11
+ total: 'X-Paginate-Elements',
12
+ pages: 'X-Paginate-Pages'
13
+ }
14
+ # Pagination attribute limit
15
+ # @return [Integer] pagination
16
+ attr_reader :page, :limit, :pages, :total
17
+ # Pagination attribute offset
18
+ # @return [Integer] pagination
19
+ def offset
20
+ (page - 1) * limit
21
+ end
22
+ # Sets a total elements for pagination
23
+ # @param total [Integer] a total number of elements
24
+ # @return [nil]
25
+ def total=(total)
26
+ @total = total.to_i
27
+ @pages = (@total / @limit.to_f).ceil
28
+ nil
29
+ end
30
+ # A constructor
31
+ #
32
+ # @param page [Integer] page to fetch
33
+ # @param limit [Integer] pagination limit
34
+ def initialize(page, limit)
35
+ msg = 'Invalid value for pagination'
36
+ fail ArgumentError, "#{msg} limit - #{limit.inspect}" unless limit.to_i >= 1
37
+ fail ArgumentError, "#{msg} page #{page.inspect}" if page.to_i < 1
38
+ @limit = limit.to_i
39
+ @page = page.to_i
40
+ @total = nil
41
+ @pages = nil
42
+ end
43
+ # A default pagination settings
44
+ DEFAULT = PuppetHerald::Models::Pagination.new(1, 20)
45
+ end
46
+ end
47
+ end
@@ -1,3 +1,5 @@
1
+ require 'sinatra/activerecord'
2
+
1
3
  # A module for Herald
2
4
  module PuppetHerald
3
5
  # module for models
@@ -1,3 +1,7 @@
1
+ require 'puppet-herald/models'
2
+ require 'puppet-herald/models/report'
3
+ require 'sinatra/activerecord'
4
+
1
5
  # A module for Herald
2
6
  module PuppetHerald
3
7
  # module for models
@@ -6,11 +10,54 @@ module PuppetHerald
6
10
  class Node < ActiveRecord::Base
7
11
  has_many :reports, dependent: :delete_all
8
12
 
13
+ # Paginete thru nodes reports
14
+ #
15
+ # @param pagination [PuppetHerald::Models::Pagination] a pagination
16
+ # @return [Node] fetched node
17
+ def paginate_reports(pagination)
18
+ pagination.total = no_of_reports
19
+ paginated = reports.order(time: :desc).limit(pagination.limit)
20
+ .offset(pagination.offset)
21
+ duplicate = dup
22
+ duplicate.reports = paginated
23
+ duplicate.id = id
24
+ duplicate.readonly!
25
+ duplicate
26
+ end
27
+
28
+ # Gets number of reports for node
29
+ #
30
+ # @return [Integer] number of node's reports
31
+ def no_of_reports
32
+ PuppetHerald::Models::Report.where(node_id: id).count
33
+ end
34
+
35
+ # Deletes nodes that doesn't have reports
36
+ #
37
+ # @return [Integer] number of empty node deleted
38
+ def self.delete_empty
39
+ joinsql = 'LEFT JOIN "reports" ON "reports"."node_id" = "nodes"."id"'
40
+ wheresql = '"reports"."node_id" IS NULL'
41
+ joins(joinsql).where(wheresql).delete_all if joins(joinsql).where(wheresql).count > 0
42
+ end
43
+
9
44
  # Gets a node with prefetched reports
45
+ #
10
46
  # @param id [Integer] a in of node to get
47
+ # @param pagination [PuppetHerald::Models::Pagination] a pagination
11
48
  # @return [Node, nil] fetched node or nil
12
- def self.get_with_reports(id)
13
- Node.joins(:reports).includes(:reports).find_by_id(id)
49
+ def self.with_reports(id, pagination = PuppetHerald::Models::Pagination::DEFAULT)
50
+ node = find_by_id(id)
51
+ node.paginate_reports pagination unless node.nil?
52
+ end
53
+
54
+ # Gets a paginated nodes
55
+ #
56
+ # @param pagination [PuppetHerald::Models::Pagination] a pagination
57
+ # @return [Node[]] nodes
58
+ def self.paginate(pagination)
59
+ pagination.total = count
60
+ order(last_run: :desc).limit(pagination.limit).offset(pagination.offset)
14
61
  end
15
62
  end
16
63
  end
@@ -1,6 +1,7 @@
1
1
  require 'puppet-herald/models/log-entry'
2
2
  require 'puppet-herald/models/node'
3
3
  require 'puppet-herald/stubs/puppet'
4
+ require 'sinatra/activerecord'
4
5
 
5
6
  # A module for Herald
6
7
  module PuppetHerald
@@ -15,7 +16,7 @@ module PuppetHerald
15
16
  # Gets a report with prefetched log entries
16
17
  # @param id [Integer] a in of report to get
17
18
  # @return [Report, nil] fetched report or nil
18
- def get_with_log_entries(id)
19
+ def with_log_entries(id)
19
20
  Report.joins(:log_entries).includes(:log_entries).find_by_id(id)
20
21
  end
21
22
 
@@ -24,17 +25,38 @@ module PuppetHerald
24
25
  # @return [Report] created report
25
26
  def create_from_yaml(yaml)
26
27
  parsed = parse_yaml yaml
27
- report = Report.new
28
+ report = nil
29
+ transaction do
30
+ report = Report.new
28
31
 
29
- parse_properties parsed, report
30
- parse_logs parsed, report
31
- node = parse_node parsed, report
32
+ parse_properties parsed, report
33
+ parse_logs parsed, report
34
+ node = parse_node parsed, report
32
35
 
33
- report.save
34
- node.save
36
+ report.save
37
+ node.save
38
+ end
35
39
  report
36
40
  end
37
41
 
42
+ # Purges older reports then given date
43
+ #
44
+ # @param date [DateTime] a date that will be border to
45
+ # @return [Integer] number of
46
+ def purge_older_then(date)
47
+ deleted = 0
48
+ query = ['"reports"."time" < ?', date]
49
+ return 0 if where(query).count == 0
50
+ transaction do
51
+ idss = joins(:log_entries).where(query).collect(&:id).uniq
52
+ PuppetHerald::Models::LogEntry.where(['"report_id" IN (?)', idss]).delete_all unless idss.empty?
53
+ where(['"id" IN (?)', idss]).delete_all unless idss.empty?
54
+ PuppetHerald::Models::Node.delete_empty
55
+ deleted = idss.length
56
+ end
57
+ deleted
58
+ end
59
+
38
60
  private
39
61
 
40
62
  def parse_node(parsed, report)
@@ -42,11 +64,9 @@ module PuppetHerald
42
64
  if node.nil?
43
65
  node = Node.new
44
66
  node.name = parsed.host
45
- node.no_of_reports = 0
46
67
  end
47
68
  report.node = node
48
69
  node.reports << report
49
- node.no_of_reports += 1
50
70
  node.status = parsed.status
51
71
  node.last_run = parsed.time
52
72
  node
@@ -62,6 +82,10 @@ module PuppetHerald
62
82
  log = LogEntry.new
63
83
  attr_to_copy = %w(level message source time)
64
84
  copy_attrs in_log, log, attr_to_copy
85
+ if log.message.include?('(noop)') && report.status != 'failed'
86
+ report.status = 'pending'
87
+ parsed.status = 'pending'
88
+ end
65
89
  log.report = report
66
90
  report.log_entries << log
67
91
  end
@@ -0,0 +1,46 @@
1
+ 'use strict';
2
+
3
+ var fs = require('fs');
4
+ var wiredep = require('wiredep');
5
+ var globby = require('globby');
6
+
7
+ var cwd = '../..';
8
+ var root = fs.realpathSync(__dirname + '/' + cwd);
9
+ var publicDir = 'lib/puppet-herald/public';
10
+ var dependencies = wiredep({
11
+ cwd: publicDir,
12
+ dependencies: true,
13
+ devDependencies: false
14
+ }).js.map(function(file) {
15
+ return file.replace(root + '/', '');
16
+ });
17
+ var devDependencies = wiredep({
18
+ cwd: 'test/javascript',
19
+ dependencies: false,
20
+ devDependencies: true,
21
+ exclude: 'angular/angular.js'
22
+ }).js.map(function(file) {
23
+ return file.replace(root + '/', '');
24
+ });
25
+ var files = {
26
+ html: globby.sync([publicDir+'/**/*.html', '!**/bower_components/**']),
27
+ js: globby.sync([publicDir+'/**/*.js', '!**/bower_components/**']),
28
+ tests: globby.sync(['test/javascript/src/**/*_test.js'])
29
+ };
30
+ var search = {
31
+ coverage: publicDir + '/!(*bower_components)/**/*.js',
32
+ html2js: publicDir + '/!(*bower_components)/**/*.html'
33
+ };
34
+ var preprocessors = {};
35
+ preprocessors[search.coverage] = ['coverage'];
36
+ preprocessors[search.html2js] = ['ng-html2js'];
37
+
38
+ module.exports = {
39
+ cwd: cwd,
40
+ root: root,
41
+ publicDir: publicDir,
42
+ dependencies: dependencies,
43
+ devDependencies: devDependencies,
44
+ preprocessors: preprocessors,
45
+ files: files
46
+ };
@@ -2,8 +2,10 @@
2
2
  'use strict';
3
3
 
4
4
  var app = angular.module('herald' , [
5
- 'ngRoute',
5
+ 'ui.router',
6
+ 'herald.router',
6
7
  'herald.page',
8
+ 'herald.settings',
7
9
  'herald.nodes',
8
10
  'herald.node',
9
11
  'herald.report',
@@ -11,17 +13,17 @@
11
13
  'herald.artifact'
12
14
  ]);
13
15
 
14
- app.config(['$routeProvider', function($routeProvider) {
15
- $routeProvider.otherwise({redirectTo: '/nodes'});
16
- }]);
16
+ app.controller('AppController',
17
+ ['Page', '$rootScope', 'Settings', '$scope',
18
+ function(Page, $rootScope, Settings, $scope) {
17
19
 
18
- app.controller('AppController', ['Page', '$rootScope', function(Page, $rootScope) {
19
20
  var ctrl = this;
20
- this.page = null;
21
- this.target = null;
21
+ $scope.page = null;
22
+ $scope.target = null;
23
+ $scope.settings = Settings;
22
24
  $rootScope.$on('Page::titleChanged', function(event, title, target) {
23
- ctrl.page = title;
24
- ctrl.target = target;
25
+ $scope.page = title;
26
+ $scope.target = target;
25
27
  });
26
28
  }]);
27
29
 
@@ -1,13 +1,20 @@
1
1
  {
2
2
  "name": "puppet-herald",
3
3
  "description": "Report processor for Puppet",
4
- "version": "0.1.0",
4
+ "version": "0.2.0",
5
5
  "homepage": "https://github.com/wavesoftware/gem-puppet-herald",
6
- "license": "Apache 2.0",
6
+ "license": "Apache-2.0",
7
7
  "private": false,
8
8
  "dependencies": {
9
9
  "angular": "1.3.x",
10
- "angular-route": "1.3.x",
10
+ "angular-moment": "0.10.x",
11
+ "angular-ui-router": "0.2.x",
12
+ "bootstrap": "3.3.x",
13
+ "angular-utils-pagination": "0.6.x",
14
+ "angular-breadcrumb": "0.3.x",
15
+ "ngstorage": "0.3.x"
16
+ },
17
+ "devDependencies": {
11
18
  "angular-loader": "1.3.x",
12
19
  "angular-mocks": "1.3.x"
13
20
  }
@@ -1,3 +1,5 @@
1
+ (function(){
2
+
1
3
  'use strict';
2
4
 
3
5
  angular.module('herald.artifact.artifact-directive', [])
@@ -10,3 +12,5 @@ angular.module('herald.artifact.artifact-directive', [])
10
12
  template: '<span>{{ artifact.version }}</span>'
11
13
  };
12
14
  });
15
+
16
+ })();
@@ -1,5 +1,3 @@
1
- 'use strict';
2
-
3
1
  (function(){
4
2
 
5
3
  'use strict';
@@ -25,7 +23,7 @@
25
23
  self.package = data.package;
26
24
  self.license = data.license;
27
25
  self.name = data.name;
28
- })
26
+ });
29
27
  }]);
30
28
 
31
29
  })();
@@ -1,5 +1,9 @@
1
+ (function(){
2
+
1
3
  'use strict';
2
4
 
3
5
  angular.module('herald.directives', [
4
6
  'herald.directives.status-button'
5
- ]);
7
+ ]);
8
+
9
+ })();
@@ -3,5 +3,5 @@
3
3
  btn btn-xs btn-{{ status | colorizeStatus }}
4
4
  glyphicon glyphicon-{{ status | iconizeStatus }}
5
5
  "
6
- ng-click="navigate(route, id)"
6
+ ng-click="navigate(route, idname, id)"
7
7
  aria-hidden="true"> {{ status | uppercase }}</button>
@@ -2,27 +2,27 @@
2
2
 
3
3
  'use strict';
4
4
 
5
- var module = angular.module('herald.directives.status-button', [
5
+ var module = angular.module('herald.directives.status-button', [ 'ui.router' ]);
6
6
 
7
- ]);
7
+ module.controller('StatusButtonController', ['$state', '$scope', function($state, $scope) {
8
8
 
9
- module.controller('StatusButtonController', ['$location', '$scope', function($location, $scope) {
9
+ $scope.$state = $state;
10
10
 
11
- $scope.$location = $location;
12
-
13
- $scope.navigate = function(route, id) {
14
- var target = route.replace(':id', id);
15
- this.$location.path(target);
11
+ $scope.navigate = function(route, idName, id) {
12
+ var params = {};
13
+ params[idName] = id;
14
+ this.$state.go(route, params);
16
15
  };
17
16
 
18
17
  }]);
19
18
 
20
- module.directive('ngStatusButton', function() {
19
+ module.directive('wsStatusButton', function() {
21
20
  return {
22
21
  restrict: 'E',
23
22
  scope: {
24
23
  status: '=',
25
24
  id: '=',
25
+ idname: '=',
26
26
  route: '='
27
27
  },
28
28
  controller: 'StatusButtonController',
@@ -39,7 +39,7 @@
39
39
  case 'pending': return 'warning';
40
40
  default: return 'default';
41
41
  }
42
- }
42
+ };
43
43
  });
44
44
 
45
45
  module.filter('iconizeStatus', function() {
@@ -51,6 +51,6 @@
51
51
  case 'pending': return 'asterisk';
52
52
  default: return 'sign';
53
53
  }
54
- }
54
+ };
55
55
  });
56
56
  })();