puppet-herald 0.2.0 → 0.8.0

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