nodectl 0.2.4

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