nodectl 0.2.4

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 (111) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/CHANGELOG.md +24 -0
  5. data/Gemfile +4 -0
  6. data/README.md +3 -0
  7. data/Rakefile +1 -0
  8. data/TODO.md +7 -0
  9. data/assets/javascript/application.coffee +22 -0
  10. data/assets/javascript/bootstrap.js +2006 -0
  11. data/assets/javascript/controllers/application_controller.coffee +73 -0
  12. data/assets/javascript/controllers/service_controller.coffee +62 -0
  13. data/assets/javascript/event_handler.coffee +51 -0
  14. data/assets/javascript/models/action.coffee +28 -0
  15. data/assets/javascript/models/instance.coffee +7 -0
  16. data/assets/javascript/models/service.coffee +75 -0
  17. data/assets/javascript/models/version.coffee +3 -0
  18. data/assets/javascript/notifier.coffee +64 -0
  19. data/assets/javascript/router.coffee +3 -0
  20. data/assets/javascript/routes/application.coffee +6 -0
  21. data/assets/javascript/routes/log.coffee +4 -0
  22. data/assets/javascript/routes/service.coffee +3 -0
  23. data/assets/javascript/store.coffee +32 -0
  24. data/assets/javascript/templates/application.hbs +82 -0
  25. data/assets/javascript/templates/index.hbs +1 -0
  26. data/assets/javascript/templates/log.hbs +1 -0
  27. data/assets/javascript/templates/service.hbs +110 -0
  28. data/assets/javascript/templates/views/action.hbs +20 -0
  29. data/assets/javascript/templates/views/actions_list.hbs +26 -0
  30. data/assets/javascript/templates/views/environments_list.hbs +9 -0
  31. data/assets/javascript/templates/views/install_button.hbs +1 -0
  32. data/assets/javascript/templates/views/mass_run.hbs +30 -0
  33. data/assets/javascript/templates/views/notifier_control.hbs +7 -0
  34. data/assets/javascript/templates/views/param.hbs +6 -0
  35. data/assets/javascript/templates/views/terminal.hbs +8 -0
  36. data/assets/javascript/ui.coffee +24 -0
  37. data/assets/javascript/vendor/ansiparse.js +186 -0
  38. data/assets/javascript/vendor/audio.min.js +24 -0
  39. data/assets/javascript/vendor/audiojs.swf +0 -0
  40. data/assets/javascript/vendor/ember-data.js +10528 -0
  41. data/assets/javascript/vendor/ember.js +40583 -0
  42. data/assets/javascript/vendor/handlebars.js +2746 -0
  43. data/assets/javascript/vendor/jquery.js +8829 -0
  44. data/assets/javascript/vendor/ladda.js +157 -0
  45. data/assets/javascript/vendor/moment.min.js +6 -0
  46. data/assets/javascript/vendor/notify-osd.js +319 -0
  47. data/assets/javascript/vendor/player-graphics.gif +0 -0
  48. data/assets/javascript/vendor/spin.js +218 -0
  49. data/assets/javascript/views/action_view.coffee +18 -0
  50. data/assets/javascript/views/actions_list_view.coffee +2 -0
  51. data/assets/javascript/views/environments_list_view.coffee +2 -0
  52. data/assets/javascript/views/install_button_view.coffee +34 -0
  53. data/assets/javascript/views/mass_run_view.coffee +107 -0
  54. data/assets/javascript/views/notifier_control_view.coffee +29 -0
  55. data/assets/javascript/views/terminal_view.coffee +56 -0
  56. data/assets/stylesheets/application.css +137 -0
  57. data/assets/stylesheets/bootstrap-theme.css +347 -0
  58. data/assets/stylesheets/bootstrap.css +5785 -0
  59. data/assets/stylesheets/fonts/glyphicons-halflings-regular.eot +0 -0
  60. data/assets/stylesheets/fonts/glyphicons-halflings-regular.svg +229 -0
  61. data/assets/stylesheets/fonts/glyphicons-halflings-regular.ttf +0 -0
  62. data/assets/stylesheets/fonts/glyphicons-halflings-regular.woff +0 -0
  63. data/assets/stylesheets/ladda-themeless.css +330 -0
  64. data/assets/stylesheets/ladda.css +392 -0
  65. data/assets/stylesheets/notify-osd.css +49 -0
  66. data/assets/stylesheets/terminal.css +12 -0
  67. data/bin/nodectl +4 -0
  68. data/lib/nodectl.rb +137 -0
  69. data/lib/nodectl/action.rb +80 -0
  70. data/lib/nodectl/binding.rb +13 -0
  71. data/lib/nodectl/cli.rb +123 -0
  72. data/lib/nodectl/context.rb +34 -0
  73. data/lib/nodectl/database.rb +55 -0
  74. data/lib/nodectl/generators/init.rb +22 -0
  75. data/lib/nodectl/generators/templates/init/.gitignore +5 -0
  76. data/lib/nodectl/generators/templates/init/config/manifest.yml +1 -0
  77. data/lib/nodectl/generators/templates/init/config/server.yml +7 -0
  78. data/lib/nodectl/instance.rb +100 -0
  79. data/lib/nodectl/log.rb +13 -0
  80. data/lib/nodectl/manager.rb +71 -0
  81. data/lib/nodectl/multi_io.rb +16 -0
  82. data/lib/nodectl/options.rb +47 -0
  83. data/lib/nodectl/process.rb +145 -0
  84. data/lib/nodectl/promised_file.rb +37 -0
  85. data/lib/nodectl/recipe.rb +197 -0
  86. data/lib/nodectl/repository.rb +80 -0
  87. data/lib/nodectl/server.rb +103 -0
  88. data/lib/nodectl/service.rb +126 -0
  89. data/lib/nodectl/stream/buffer.rb +44 -0
  90. data/lib/nodectl/stream/events_session.rb +39 -0
  91. data/lib/nodectl/stream/file.rb +69 -0
  92. data/lib/nodectl/stream/file_session.rb +35 -0
  93. data/lib/nodectl/stream/file_with_memory.rb +17 -0
  94. data/lib/nodectl/stream/websocket.rb +90 -0
  95. data/lib/nodectl/version.rb +3 -0
  96. data/lib/nodectl/watchdog.rb +17 -0
  97. data/lib/nodectl/webapp/actions.rb +21 -0
  98. data/lib/nodectl/webapp/base.rb +29 -0
  99. data/lib/nodectl/webapp/instances.rb +46 -0
  100. data/lib/nodectl/webapp/public/sounds/alert.mp3 +0 -0
  101. data/lib/nodectl/webapp/public/sounds/error.mp3 +0 -0
  102. data/lib/nodectl/webapp/public/sounds/question.mp3 +0 -0
  103. data/lib/nodectl/webapp/services.rb +39 -0
  104. data/lib/nodectl/webapp/settings.rb +7 -0
  105. data/lib/nodectl/webapp/ui.rb +19 -0
  106. data/lib/nodectl/webapp/versions.rb +12 -0
  107. data/lib/nodectl/webapp/views/ui.haml +20 -0
  108. data/nodectl.gemspec +35 -0
  109. data/spec/spec_helper.rb +12 -0
  110. data/spec/stream/buffer_spec.rb +33 -0
  111. metadata +365 -0
@@ -0,0 +1 @@
1
+ Hello world
@@ -0,0 +1 @@
1
+ {{view UI.TerminalView path=path}}
@@ -0,0 +1,110 @@
1
+ {{#if isInstalled}}
2
+ <div class="page-header">
3
+ <h2>{{id}} <small>powered by {{recipe_name}} recipe</small></h2>
4
+ </div>
5
+
6
+ {{#if version_id}}
7
+
8
+ <p>
9
+ <span class="label label-primary">{{version_id}}</span> <small><b>{{version_timestamp}}</b> {{version_message}}</small>
10
+ </p>
11
+ <p>
12
+ <button class="btn btn-success btn-xs" {{action 'setLatestVersion' }}>Upgrade to latest</button>
13
+ <button class="btn btn-default btn-xs" {{action 'versions_toggle'}}>Show all versions</button>
14
+ </p>
15
+
16
+ {{#if versions_opened}}
17
+ <ul>
18
+ {{#each versions}}
19
+ <li><small><button class="btn btn-success btn-xs" {{action 'setVersion' id }}>{{id}}</button> <em>({{timestamp}}) -- {{message}}</em></small></li>
20
+ {{/each}}
21
+ </ul>
22
+ {{/if}}
23
+
24
+ <hr>
25
+ {{/if}}
26
+
27
+ <div class="panel panel-default">
28
+ <div class="panel-heading">
29
+ <h4 class="panel-title">Actions</h4>
30
+ </div>
31
+
32
+ <div class="panel-body">
33
+ <div class="btn-group">
34
+ {{#each supported_actions}}
35
+ <button class="btn btn-default btn-xs" {{action 'run_action' this }}>{{this}}</button>
36
+ {{/each}}
37
+ </div>
38
+ </div>
39
+ </div>
40
+
41
+ <hr>
42
+
43
+ {{#if start_supported}}
44
+
45
+ <div class="panel panel-default">
46
+ <div class="panel-heading">
47
+ <h4 class="panel-title pull-left">Instances</h4> <button class="btn btn-success btn-xs pull-right" {{action 'startInstance'}}>start</button>
48
+ <div class="clearfix"></div>
49
+ </div>
50
+
51
+ <div class="panel-body">
52
+
53
+ <table class="table table-condensed table-hover instances-table">
54
+ <thead>
55
+ <tr>
56
+ <th class="instances-table-pid">PID</td>
57
+ <th class="instances-table-status">Status</td>
58
+ <th class="instances-table-environment">Environment</td>
59
+ <th class="instances-table-actions">Actions</td>
60
+ </tr>
61
+ </thead>
62
+ <tbody>
63
+ {{#each instances}}
64
+ <tr {{bind-attr class=status}}>
65
+ <td>{{id}}</td>
66
+ <td>{{status}}</td>
67
+ <td>{{options.env}}</td>
68
+ <td>
69
+ {{#if service.stop_supported}}
70
+ <button class="btn btn-primary btn-xs" {{action 'stopInstance' this}}>stop</button>
71
+ <button class="btn btn-warning btn-xs" {{action 'restartInstance' this}}>restart</button>
72
+ <button class="btn btn-danger btn-xs" {{action 'killInstance' this}}>kill</button>
73
+ {{/if}}
74
+ </td>
75
+ </tr>
76
+ {{/each}}
77
+ </tbody>
78
+ </table>
79
+ </div>
80
+ </div>
81
+ {{/if}}
82
+
83
+ <div class="panel panel-default">
84
+ <div class="panel-heading">
85
+ <h4 class="panel-title">Logs</h4>
86
+ </div>
87
+
88
+ <div class="panel-body">
89
+ <ul class="nav nav-pills">
90
+ {{#each logs}}
91
+ {{#link-to 'log' name tagName="li"}}
92
+ <!-- TODO: fix bug with assign 'active' class on page update -->
93
+ {{#link-to 'log' name}}{{name}}{{/link-to}}
94
+ {{/link-to}}
95
+ {{/each}}
96
+ </ul>
97
+
98
+ {{outlet}}
99
+ </div>
100
+ </div>
101
+
102
+ {{else}}
103
+
104
+ <div class="jumbotron">
105
+ <h1>{{id}} <small>powered by {{recipe_name}} recipe</small></h1>
106
+ <p>This service is not installed yet.</p>
107
+ <p><button class="btn btn-primary btn-lg" role="button" {{action 'installService'}}>Install</button></p>
108
+ </div>
109
+
110
+ {{/if}}
@@ -0,0 +1,20 @@
1
+ <div class="modal-window-content">
2
+ <p>
3
+ <span class="label label-primary">{{view.action.name}} for service {{view.action.service.id}}</span>
4
+ {{#if view.action.result}}
5
+ {{#if view.action.isSucceed}}
6
+ <span class="label label-success">succeed</span>
7
+ {{else}}
8
+ <span class="label label-danger">failed</span>
9
+ {{/if}}
10
+ {{else}}
11
+ <span class="label label-default">running</span>
12
+ {{/if}}
13
+ <small>{{view.action.started_at_formatted}}</small>
14
+ </p>
15
+ <div class="terminal-wrapper">
16
+ {{#if view.action.ws}}
17
+ {{view UI.TerminalView path=view.action.ws class="action-terminal"}}
18
+ {{/if}}
19
+ </div>
20
+ </div>
@@ -0,0 +1,26 @@
1
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown">
2
+ <span class="glyphicon glyphicon-th-large"></span> actions <b class="caret"></b>
3
+ </a>
4
+
5
+ <ul class="dropdown-menu">
6
+ <!-- TODO
7
+ <li>
8
+ <div class="btn-group btn-group-justified btn-group-actions">
9
+ <div class="btn-group btn-group-xs">
10
+ <button type="button" class="btn btn-default">Clear</button>
11
+ </div>
12
+ <div class="btn-group btn-group-xs">
13
+ <button type="button" class="btn btn-default">Read all</button>
14
+ </div>
15
+ </div>
16
+
17
+ </li>
18
+ -->
19
+
20
+ {{#each running_actions}}
21
+ <li {{bind-attr class=":action-list-item isSucceed:action-list-item-succeed isFailed:action-list-item-failed"}}><a href="#" {{action 'openAction' this}}><span class="label label-default">{{started_at_formatted}}</span> {{service.id}}: {{name}}</a></li>
22
+ {{else}}
23
+ <li>No actions yet</li>
24
+ {{/each}}
25
+ </ul>
26
+
@@ -0,0 +1,9 @@
1
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown">
2
+ <span class="glyphicon glyphicon-th-large"></span> {{UI.currentEnvironment}} <b class="caret"></b>
3
+ </a>
4
+
5
+ <ul class="dropdown-menu">
6
+ {{#each environments}}
7
+ <li><a href="#" {{action 'setEnvironment' this}}>{{this}}</a></li>
8
+ {{/each}}
9
+ </ul>
@@ -0,0 +1 @@
1
+ <button class="btn btn-success btn-xs ladda-button" data-size="xs" data-style="expand-left" {{action 'installService' target=view}}>Install{{#unless view.service}} all{{/unless}}</button>
@@ -0,0 +1,30 @@
1
+ <div class="list-group-item list-group-item-service mass-run-list-item list-group-item-service-controlls">
2
+ <div class="btn-group">
3
+ <button class="btn btn-success btn-xs" {{action "runSelected" target=view}}>Run!</button>
4
+ <button class="btn btn-primary btn-xs" {{action "selectIdle" target=view}}>Select idle</button>
5
+ </div>
6
+
7
+ {{view view.selectAllCheckbox class="checkbox" list=view.servicesSelected services=view.servicesRunnable parent=view}}
8
+
9
+ <button class="btn btn-danger btn-xs pull-right" {{action 'toggleMassRunOpened'}}>Cancel</button>
10
+ </div>
11
+
12
+ {{#each view.services}}
13
+ <label {{bind-attr class=":list-group-item :list-group-item-service :mass-run-list-item isRunning"}}>
14
+ {{#if isRunning}}
15
+ <div class="service-run-indicator">&nbsp</div>
16
+ {{/if}}
17
+
18
+ {{#if start_supported}}
19
+ {{view view.runCheckbox service=this list=view.servicesSelected class="checkbox"}}
20
+ {{/if}}
21
+
22
+ <div class="sidebar-services-id">
23
+ {{id}}
24
+ </div>
25
+
26
+ {{#if start_supported}}
27
+ <span class="badge">{{instances.length}}</span>
28
+ {{/if}}
29
+ </label>
30
+ {{/each}}
@@ -0,0 +1,7 @@
1
+ <li>
2
+ <a href="#" {{action "toggleSound" target="view"}} {{bind-attr class="view.soundEnabled::notify-control-disabled"}}><span {{bind-attr class=":glyphicon view.soundEnabled:glyphicon-ok:glyphicon-remove"}}></span> Sounds</b></a>
3
+ </li>
4
+
5
+ <li>
6
+ <a href="#" {{action "togglePopup" target="view"}} {{bind-attr class="view.popupEnabled::notify-control-disabled"}}><span {{bind-attr class=":glyphicon view.popupEnabled:glyphicon-ok:glyphicon-remove"}}></span> Popups</b></a>
7
+ </li>
@@ -0,0 +1,6 @@
1
+
2
+ {{#if choices }}
3
+ {{view Ember.Select content=view.inst_options.choices optionValuePath="content" optionLabelPath="content" value=view.value selection=view.inst_options.default class="form-control input-sm"}}
4
+ {{else}}
5
+ <input class="form-control input-sm" {{bind-attr 'placeholer' name}}>
6
+ {{/if}}
@@ -0,0 +1,8 @@
1
+ <div class="terminal">
2
+ <div class="terminal-output"></div>
3
+
4
+ <div class="terminal-controls">
5
+ {{view.status}}
6
+ <button class="btn btn-danger btn-xs pull-right" {{action cleanup target=view}}>cleanup</button>
7
+ </div>
8
+ </div>
@@ -0,0 +1,24 @@
1
+ #= require ./vendor/jquery
2
+ #= require ./vendor/handlebars
3
+ #= require ./vendor/ember
4
+ #= require ./vendor/ember-data
5
+ #= require ./vendor/ansiparse
6
+ #= require ./vendor/spin
7
+ #= require ./vendor/ladda
8
+ #= require ./vendor/moment.min
9
+ #= require ./vendor/audio.min
10
+ #= require ./vendor/notify-osd
11
+
12
+ #= require ./application
13
+ #= require ./store
14
+ #= require ./router
15
+
16
+ #= require_tree ./templates
17
+ #= require_tree ./models
18
+ #= require_tree ./controllers
19
+ #= require_tree ./routes
20
+ #= require_tree ./views
21
+
22
+ #= require ./bootstrap
23
+ #= require ./event_handler
24
+ #= require ./notifier
@@ -0,0 +1,186 @@
1
+ ansiparse = function (str) {
2
+ //
3
+ // I'm terrible at writing parsers.
4
+ //
5
+ var matchingControl = null,
6
+ matchingData = null,
7
+ matchingText = '',
8
+ ansiState = [],
9
+ result = [],
10
+ state = {},
11
+ eraseChar;
12
+
13
+ //
14
+ // General workflow for this thing is:
15
+ // \033\[33mText
16
+ // | | |
17
+ // | | matchingText
18
+ // | matchingData
19
+ // matchingControl
20
+ //
21
+ // In further steps we hope it's all going to be fine. It usually is.
22
+ //
23
+
24
+ //
25
+ // Erases a char from the output
26
+ //
27
+ eraseChar = function () {
28
+ var index, text;
29
+ if (matchingText.length) {
30
+ matchingText = matchingText.substr(0, matchingText.length - 1);
31
+ }
32
+ else if (result.length) {
33
+ index = result.length - 1;
34
+ text = result[index].text;
35
+ if (text.length === 1) {
36
+ //
37
+ // A result bit was fully deleted, pop it out to simplify the final output
38
+ //
39
+ result.pop();
40
+ }
41
+ else {
42
+ result[index].text = text.substr(0, text.length - 1);
43
+ }
44
+ }
45
+ };
46
+
47
+ for (var i = 0; i < str.length; i++) {
48
+ if (matchingControl != null) {
49
+ if (matchingControl == '\033' && str[i] == '\[') {
50
+ //
51
+ // We've matched full control code. Lets start matching formating data.
52
+ //
53
+
54
+ //
55
+ // "emit" matched text with correct state
56
+ //
57
+ if (matchingText) {
58
+ state.text = matchingText;
59
+ result.push(state);
60
+ state = {};
61
+ matchingText = "";
62
+ }
63
+
64
+ matchingControl = null;
65
+ matchingData = '';
66
+ }
67
+ else {
68
+ //
69
+ // We failed to match anything - most likely a bad control code. We
70
+ // go back to matching regular strings.
71
+ //
72
+ matchingText += matchingControl + str[i];
73
+ matchingControl = null;
74
+ }
75
+ continue;
76
+ }
77
+ else if (matchingData != null) {
78
+ if (str[i] == ';') {
79
+ //
80
+ // `;` separates many formatting codes, for example: `\033[33;43m`
81
+ // means that both `33` and `43` should be applied.
82
+ //
83
+ // TODO: this can be simplified by modifying state here.
84
+ //
85
+ ansiState.push(matchingData);
86
+ matchingData = '';
87
+ }
88
+ else if (str[i] == 'm') {
89
+ //
90
+ // `m` finished whole formatting code. We can proceed to matching
91
+ // formatted text.
92
+ //
93
+ ansiState.push(matchingData);
94
+ matchingData = null;
95
+ matchingText = '';
96
+
97
+ //
98
+ // Convert matched formatting data into user-friendly state object.
99
+ //
100
+ // TODO: DRY.
101
+ //
102
+ ansiState.forEach(function (ansiCode) {
103
+ if (ansiparse.foregroundColors[ansiCode]) {
104
+ state.foreground = ansiparse.foregroundColors[ansiCode];
105
+ }
106
+ else if (ansiparse.backgroundColors[ansiCode]) {
107
+ state.background = ansiparse.backgroundColors[ansiCode];
108
+ }
109
+ else if (ansiCode == 39) {
110
+ delete state.foreground;
111
+ }
112
+ else if (ansiCode == 49) {
113
+ delete state.background;
114
+ }
115
+ else if (ansiparse.styles[ansiCode]) {
116
+ state[ansiparse.styles[ansiCode]] = true;
117
+ }
118
+ else if (ansiCode == 22) {
119
+ state.bold = false;
120
+ }
121
+ else if (ansiCode == 23) {
122
+ state.italic = false;
123
+ }
124
+ else if (ansiCode == 24) {
125
+ state.underline = false;
126
+ }
127
+ });
128
+ ansiState = [];
129
+ }
130
+ else {
131
+ matchingData += str[i];
132
+ }
133
+ continue;
134
+ }
135
+
136
+ if (str[i] == '\033') {
137
+ matchingControl = str[i];
138
+ }
139
+ else if (str[i] == '\u0008') {
140
+ eraseChar();
141
+ }
142
+ else {
143
+ matchingText += str[i];
144
+ }
145
+ }
146
+
147
+ if (matchingText) {
148
+ state.text = matchingText + (matchingControl ? matchingControl : '');
149
+ result.push(state);
150
+ }
151
+ return result;
152
+ }
153
+
154
+ ansiparse.foregroundColors = {
155
+ '30': 'black',
156
+ '31': 'red',
157
+ '32': 'green',
158
+ '33': 'yellow',
159
+ '34': 'blue',
160
+ '35': 'magenta',
161
+ '36': 'cyan',
162
+ '37': 'white',
163
+ '90': 'grey'
164
+ };
165
+
166
+ ansiparse.backgroundColors = {
167
+ '40': 'black',
168
+ '41': 'red',
169
+ '42': 'green',
170
+ '43': 'yellow',
171
+ '44': 'blue',
172
+ '45': 'magenta',
173
+ '46': 'cyan',
174
+ '47': 'white'
175
+ };
176
+
177
+ ansiparse.styles = {
178
+ '1': 'bold',
179
+ '3': 'italic',
180
+ '4': 'underline'
181
+ };
182
+
183
+ if (typeof module == "object" && typeof window == "undefined") {
184
+ module.exports = ansiparse;
185
+ }
186
+