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,3 +1,5 @@
1
+ (function(){
2
+
1
3
  'use strict';
2
4
 
3
5
  angular.module('herald.filters', [])
@@ -5,7 +7,7 @@ angular.module('herald.filters', [])
5
7
  .filter('capitalize', function() {
6
8
  return function(input) {
7
9
  var text;
8
- if (input == null) {
10
+ if (input === null || input === undefined) {
9
11
  text = '';
10
12
  } else {
11
13
  text = input.toString();
@@ -14,4 +16,6 @@ angular.module('herald.filters', [])
14
16
  var rest = text.substring(1);
15
17
  return first.toUpperCase() + rest;
16
18
  };
17
- });
19
+ });
20
+
21
+ })();
@@ -5,13 +5,13 @@
5
5
 
6
6
  module.factory('Page', ['$document', '$rootScope', function($document, $rootScope) {
7
7
  var root = $document;
8
- var base = root[0].title
8
+ var base = root[0].title;
9
9
  var title = null;
10
10
  var target = null;
11
11
  var service = {
12
12
  title: function(newTitle, newTarget, joiner) {
13
13
  var merged = newTitle + '';
14
- if(typeof(joiner) === 'undefined') joiner = ': ';
14
+ if(typeof(joiner) === 'undefined') { joiner = ': '; }
15
15
  if (typeof(newTarget) !== 'undefined') {
16
16
  merged = merged + joiner + newTarget;
17
17
  }
@@ -0,0 +1,142 @@
1
+ (function(){
2
+ 'use strict';
3
+
4
+ angular.module('herald.pagination' , ['angularUtils.directives.dirPagination'])
5
+
6
+ .config(['paginationTemplateProvider', function(paginationTemplateProvider) {
7
+ paginationTemplateProvider.setPath('/bower_components/angular-utils-pagination/dirPagination.tpl.html');
8
+ }])
9
+
10
+ .factory('PaginationFactory', function() {
11
+ var KEYS = {
12
+ page: 'X-Paginate-Page',
13
+ limit: 'X-Paginate-Limit',
14
+ elements: 'X-Paginate-Elements',
15
+ pages: 'X-Paginate-Pages'
16
+ };
17
+ function isNotNil(testable) {
18
+ return testable !== null && testable !== undefined;
19
+ }
20
+ function Pagination(page, limit, elements, pages) {
21
+ function isNumeric(input) {
22
+ return !isNaN(input) && input !== '';
23
+ }
24
+ var self = {
25
+ page: parseInt(page),
26
+ limit: parseInt(limit),
27
+ elements: null,
28
+ pages: null,
29
+ listener: null,
30
+ this: this
31
+ };
32
+ if (!isNumeric(page) || self.page < 1) {
33
+ throw new TypeError('Value for pagination page is invalid: ' + page);
34
+ }
35
+ if (!isNumeric(limit) || self.limit < 1) {
36
+ throw new TypeError('Value for pagination limit is invalid: ' + limit);
37
+ }
38
+ if (isNotNil(elements) && isNumeric(elements)) { self.elements = parseInt(elements); }
39
+ if (isNotNil(pages) && isNumeric(pages)) { self.pages = parseInt(pages); }
40
+ function verifyPage(candidate) {
41
+ var icand = parseInt(candidate);
42
+ if (!isNumeric(candidate) || icand < 1) {
43
+ throw new TypeError('Value for pagination page is invalid: ' + candidate);
44
+ }
45
+ if (isNotNil(self.this.pages()) && icand > self.this.pages()) {
46
+ throw new TypeError('Value for pagination page is invalid: ' +
47
+ candidate + ', max page: ' + self.this.pages());
48
+ }
49
+ return icand;
50
+ }
51
+ this.page = function(set) {
52
+ if (isNotNil(set)) {
53
+ var candidate = verifyPage(set);
54
+ if (isNotNil(self.listener)) {
55
+ self.listener(candidate);
56
+ }
57
+ self.page = candidate;
58
+ }
59
+ return self.page;
60
+ };
61
+ this.next = function() {
62
+ var actual = this.page();
63
+ this.page(actual + 1);
64
+ };
65
+ this.previous = function() {
66
+ var actual = this.page();
67
+ this.page(actual - 1);
68
+ };
69
+ this.hasNext = function() {
70
+ return (isNotNil(this.pages()) && this.page()+1 <= this.pages());
71
+ };
72
+ this.hasPrevious = function() {
73
+ return (this.page()-1 >= 1);
74
+ };
75
+ this.limit = function() {
76
+ return self.limit;
77
+ };
78
+ this.elements = function(set) {
79
+ if (isNotNil(set)) {
80
+ self.elements = parseInt(set);
81
+ }
82
+ return self.elements;
83
+ };
84
+ this.pages = function(set) {
85
+ if (isNotNil(set)) {
86
+ self.pages = parseInt(set);
87
+ }
88
+ return self.pages;
89
+ };
90
+ this.toHeaders = function() {
91
+ var headers = {};
92
+ headers[KEYS.page] = this.page() + '';
93
+ headers[KEYS.limit] = this.limit() + '';
94
+ return headers;
95
+ };
96
+ this.setPageChangeListener = function(listener) {
97
+ self.listener = listener;
98
+ };
99
+ }
100
+ var DEFAULT = new Pagination(1, 15);
101
+ return {
102
+ DEFAULT: DEFAULT,
103
+ fromHeaders: function(headersGetter) {
104
+ var page = isNotNil(headersGetter(KEYS.page)) ? headersGetter(KEYS.page) : DEFAULT.page();
105
+ var limit = isNotNil(headersGetter(KEYS.limit)) ? headersGetter(KEYS.limit) : DEFAULT.limit();
106
+ var elements = headersGetter(KEYS.elements);
107
+ var pages = headersGetter(KEYS.pages);
108
+ return new Pagination(page, limit, elements, pages);
109
+ },
110
+ create: function(page, limit) {
111
+ return new Pagination(page, limit);
112
+ },
113
+ createPageCache: function(maxSeconds) {
114
+ var loaded = [];
115
+ var times = [];
116
+ function isLoaded(pageNumber) {
117
+ var now = new Date();
118
+ if (loaded[pageNumber] === undefined) {
119
+ return false;
120
+ }
121
+ var ealier = times[pageNumber];
122
+ var delta = (now - ealier) / 1000.0;
123
+ return delta <= maxSeconds;
124
+ }
125
+ return {
126
+ isLoaded: isLoaded,
127
+ get: function(pageNumber) {
128
+ if (!isLoaded(pageNumber)) {
129
+ return undefined;
130
+ }
131
+ return loaded[pageNumber];
132
+ },
133
+ set: function(pageNumber, data) {
134
+ loaded[pageNumber] = data;
135
+ times[pageNumber] = new Date();
136
+ }
137
+ };
138
+ }
139
+ };
140
+ });
141
+
142
+ })();
@@ -0,0 +1,25 @@
1
+ (function(angular){
2
+ 'use strict';
3
+
4
+ var module = angular.module('herald.settings' , ['ngStorage']);
5
+
6
+ module.factory('Settings', ['$localStorage', function($localStorage) {
7
+ var settings = {
8
+ colorSyntax: {
9
+ options: [
10
+ { label: 'Puppet 2.x', value: 'puppet2' },
11
+ { label: 'Puppet 3.x', value: 'puppet3' }
12
+ ]
13
+ },
14
+ report: {
15
+ info: false
16
+ },
17
+ pagination: {
18
+ perPage: 15
19
+ }
20
+ };
21
+ settings.colorSyntax.selected = settings.colorSyntax.options[0];
22
+ return $localStorage.$default(settings);
23
+ }]);
24
+
25
+ })(angular);
@@ -1,8 +1,97 @@
1
+ body {
2
+ background-color: #d8dee0;
3
+ }
4
+
5
+ code, kbd, pre, samp {
6
+ font-family: 'Source Code Pro', Menlo, Monaco, Consolas, monospace;
7
+ }
8
+
1
9
  .herald-table-report > tbody > tr {
2
- font-family: monospace;
10
+ font-family: 'Source Code Pro', Menlo, Monaco, Consolas, monospace;
3
11
  background-color: #2C001E;
4
- white-space: pre;
12
+ white-space: pre-wrap; /* css-3 */
13
+ white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
14
+ white-space: -pre-wrap; /* Opera 4-6 */
15
+ white-space: -o-pre-wrap; /* Opera 7 */
16
+ word-wrap: break-word; /* Internet Explorer 5.5+ */
5
17
  color: #777;
18
+ overflow: scroll;
19
+ }
20
+
21
+ .unselectable {
22
+ -webkit-touch-callout: none;
23
+ -webkit-user-select: none;
24
+ -khtml-user-select: none;
25
+ -moz-user-select: none;
26
+ -ms-user-select: none;
27
+ user-select: none;
28
+ }
29
+
30
+ .vcenter {
31
+ display: inline-block;
32
+ vertical-align: middle;
33
+ float: none;
34
+ }
35
+
36
+ .header {
37
+ padding: 0.5em;
38
+ margin: 0 -15px 1em -15px;
39
+ background-color: #444;
40
+ overflow: hidden;
41
+ }
42
+
43
+ .header .header-logo {
44
+ white-space: nowrap;
45
+ float: left;
46
+ margin-right: 0.5em;
47
+ }
48
+ .header .header-breadcrumb {
49
+ margin: 0;
50
+ overflow: hidden;
51
+ }
52
+ .header .header-settings {
53
+ width: 2.93em;
54
+ float: right;
55
+ margin-left: 1em;
56
+ }
57
+ .header .header-logo h1, .header .header-logo h2, .header .header-logo h3 {
58
+ width: 4em;
59
+ margin: 0;
60
+ font-weight: 100;
61
+ }
62
+ .header .header-logo a {
63
+ color: #eee;
64
+ }
65
+ .header .header-logo img {
66
+ height: 1em;
67
+ vertical-align: top;
68
+ padding: 0 0.1em;
69
+ }
70
+ .header .header-breadcrumb .breadcrumb {
71
+ margin-bottom: 0;
72
+ background: transparent;
73
+ }
74
+ .header .header-breadcrumb .breadcrumb a {
75
+ color: #eee;
76
+ }
77
+ .header .header-breadcrumb .breadcrumb > .active {
78
+ color: #aaa;
79
+ }
80
+
81
+ .pagination {
82
+ margin: 0;
83
+ }
84
+
85
+ .footer {
86
+ margin-top: -1em;
87
+ }
88
+
89
+ .modal {
90
+ background-color: rgba(0,0,0,0.3);
91
+ }
92
+
93
+ .ws-show-block {
94
+ display: block;
6
95
  }
7
96
 
8
97
  .table-hover.herald-table-report > tbody > tr:hover {
@@ -18,10 +107,18 @@
18
107
  }
19
108
 
20
109
  .herald-table-report > tbody > tr > td.herald-log-notice {
21
- color: #eeeeec;
110
+ color: #079898;
22
111
  }
23
112
 
24
113
  .herald-table-report > tbody > tr > td.herald-log-debug {
114
+ color: #eeeeec;
115
+ }
116
+
117
+ .puppet3.herald-table-report > tbody > tr > td.herald-log-notice {
118
+ color: #eeeeec;
119
+ }
120
+
121
+ .puppet3.herald-table-report > tbody > tr > td.herald-log-debug {
25
122
  color: #079898;
26
123
  }
27
124
 
@@ -0,0 +1,73 @@
1
+ <section class="container-fluid">
2
+
3
+ <section class="header">
4
+ <div class="header-logo">
5
+ <h2><a href="#/"><img src="/img/shield97-white.svg" />Herald</a></h2>
6
+ </div>
7
+ <div class="header-settings">
8
+ <button
9
+ type="button"
10
+ class="btn btn-info glyphicon glyphicon-cog"
11
+ ng-init="optionsShown = false"
12
+ ng-click="optionsShown = !optionsShown"></button>
13
+ </div>
14
+ <div class="header-breadcrumb">
15
+ <ncy-breadcrumb></ncy-breadcrumb>
16
+ </div>
17
+ </section>
18
+
19
+ <ui-view></ui-view>
20
+
21
+ <address class="footer">
22
+ <p class="text-muted">
23
+ <small>
24
+ Herald, version: <samp app-version></samp>
25
+ </small>
26
+ </p>
27
+ </address>
28
+
29
+ <div class="modal fade" ng-class="{in: optionsShown, 'ws-show-block': optionsShown}">
30
+ <div class="modal-dialog">
31
+ <div class="modal-content">
32
+ <div class="modal-header">
33
+ <button type="button" class="close" ng-click="optionsShown = !optionsShown"><span aria-hidden="true">&times;</span></button>
34
+ <h4 class="modal-title">Herald settings</h4>
35
+ </div>
36
+ <div class="modal-body">
37
+ <form class="form">
38
+ <div class="form-group">
39
+ <label for="settings_colorSyntax_selected">Reports color scheme</label>
40
+ <select id="settings_colorSyntax_selected"
41
+ class="form-control"
42
+ ng-model="settings.colorSyntax.selected"
43
+ ng-options="item.label for item in settings.colorSyntax.options track by item.value">
44
+ </select>
45
+ </div>
46
+ <!--
47
+ <div class="form-group">
48
+ <label for="settings_pagination_perPage">Elements per page</label>
49
+ <select id="settings_pagination_perPage"
50
+ class="form-control"
51
+ ng-model="settings.pagination.perPage">
52
+ <option>5</option>
53
+ <option>10</option>
54
+ <option>15</option>
55
+ <option>20</option>
56
+ <option>30</option>
57
+ <option>50</option>
58
+ <option>100</option>
59
+ </select>
60
+ </div>
61
+ -->
62
+ <div class="checkbox">
63
+ <label>
64
+ <input type="checkbox" ng-model="settings.report.info"> Show more information in reports
65
+ </label>
66
+ </div>
67
+ </form>
68
+ </div>
69
+ </div>
70
+ </div>
71
+ </div>
72
+
73
+ </section>
@@ -0,0 +1,53 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
+
4
+ <svg
5
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
6
+ xmlns:cc="http://creativecommons.org/ns#"
7
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8
+ xmlns:svg="http://www.w3.org/2000/svg"
9
+ xmlns="http://www.w3.org/2000/svg"
10
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
11
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12
+ version="1.1"
13
+ id="Layer_1"
14
+ x="0px"
15
+ y="0px"
16
+ width="48px"
17
+ height="48px"
18
+ viewBox="0 0 48 48"
19
+ enable-background="new 0 0 48 48"
20
+ xml:space="preserve"
21
+ inkscape:version="0.48.4 r9939"
22
+ sodipodi:docname="shield97.svg"><metadata
23
+ id="metadata13"><rdf:RDF><cc:Work
24
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
25
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
26
+ id="defs11" /><sodipodi:namedview
27
+ pagecolor="#ffffff"
28
+ bordercolor="#666666"
29
+ borderopacity="1"
30
+ objecttolerance="10"
31
+ gridtolerance="10"
32
+ guidetolerance="10"
33
+ inkscape:pageopacity="0"
34
+ inkscape:pageshadow="2"
35
+ inkscape:window-width="1039"
36
+ inkscape:window-height="774"
37
+ id="namedview9"
38
+ showgrid="false"
39
+ inkscape:zoom="4.9166667"
40
+ inkscape:cx="24"
41
+ inkscape:cy="24"
42
+ inkscape:window-x="75"
43
+ inkscape:window-y="34"
44
+ inkscape:window-maximized="0"
45
+ inkscape:current-layer="Layer_1" /><g
46
+ id="g3"
47
+ style="fill:#f2f2f2"><path
48
+ d="M44.083,7.732L24.745,1.693c-0.484-0.151-1.006-0.151-1.49,0L3.916,7.732c-1.044,0.326-1.755,1.292-1.755,2.386 c0,26.74,13.511,33.13,20.77,36.563c0.339,0.16,0.704,0.24,1.069,0.24s0.73-0.08,1.069-0.24 c7.258-3.434,20.769-9.825,20.769-36.563C45.838,9.024,45.127,8.058,44.083,7.732z M24,41.653 C17.108,38.342,7.701,32.857,7.184,11.95L24,6.698l16.815,5.251C40.298,32.856,30.892,38.341,24,41.653z"
49
+ id="path5"
50
+ style="fill:#f2f2f2" /><path
51
+ d="M23.239,13.104l-1.989,6.593l-6.886-0.145c-0.35-0.007-0.663,0.215-0.771,0.548c-0.108,0.333,0.015,0.697,0.302,0.896 l5.656,3.93l-2.266,6.504c-0.115,0.331,0,0.697,0.282,0.903c0.283,0.206,0.668,0.202,0.946-0.01l5.485-4.165l5.485,4.165 c0.278,0.211,0.663,0.215,0.946,0.01c0.282-0.206,0.397-0.572,0.282-0.903l-2.266-6.504l5.656-3.93 c0.287-0.199,0.41-0.564,0.302-0.896c-0.108-0.333-0.422-0.556-0.771-0.548l-6.886,0.145l-1.989-6.593 c-0.102-0.335-0.41-0.564-0.76-0.564S23.341,12.769,23.239,13.104z"
52
+ id="path7"
53
+ style="fill:#f2f2f2" /></g></svg>