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,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>