tmux-connector 0.9.7 → 1.0.8

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -2,8 +2,28 @@
2
2
 
3
3
  Manage multiple servers using SSH and [tmux].
4
4
 
5
+ For the demo and short intro, read [this blog post][blog-post].
6
+
7
+
8
+ - [Features](#features)
9
+ - [Quick tease](#quick-tease)
10
+ - [CLI description](#cli-description)
11
+ - [Send command](#send-command)
12
+ - [Configuration](#configuration)
13
+ - [Sessions without configuration files](#sessions-without-configuration-files)
14
+ - [Requirements](#requirements)
15
+ - [Installation](#installation)
16
+ - [Installing tmux](#installing-tmux)
17
+ - [Tips](#tips)
18
+ - [SSH config files](#ssh-config-files)
19
+ - [Tmux configuration](#tmux-configuration)
20
+ - [Sending the commands](#sending-the-commands)
21
+ - [Contributing](#contributing)
22
+ - [Comments, ideas or if you feel like chatting](#comments-ideas-or-if-you-feel-like-chatting)
23
+
24
+
25
+ ## Features
5
26
 
6
- ## Features:
7
27
  * connect to multiple servers at once
8
28
  * expressive layouts customizable for different server groups available
9
29
  * issue commands to all servers or just a selected (custom) subgroup
@@ -51,6 +71,11 @@ Manage multiple servers using SSH and [tmux].
51
71
 
52
72
  - resume (recreate) 's#3' session, even after computer restart
53
73
 
74
+ `tcon start -q "dev.node-staging-[[42,]]"`
75
+
76
+ - starts quick session (no config file); selects all 'node' servers that have
77
+ index of at least 42
78
+
54
79
 
55
80
  ## CLI description
56
81
 
@@ -63,12 +88,13 @@ even after computer restarts. Complex sessions with different layouts for
63
88
  different kinds of servers can be easily created.
64
89
 
65
90
  Usage:
66
- tcon start <config-file> [--ssh-config=<file>]
67
- [--session-name=<name>] [--purpose=<description>]
91
+ tcon start ( <config-file> | --quick-session=<qs-args> )
92
+ [--ssh-config=<file>] [--session-name=<name>]
93
+ [--purpose=<description>]
68
94
  tcon resume <session-name>
69
95
  tcon delete (<session-name> | --all)
70
96
  tcon list
71
- tcon send <session-name> (<command> | --command-file=<file>)
97
+ tcon send <session-name> ( <command> | --command-file=<file> )
72
98
  [ --server-filter=<filter> | --group-filter=<regex>
73
99
  | --filter=<regex> | --window=<index> ]
74
100
  [--verbose]
@@ -76,35 +102,39 @@ Usage:
76
102
  tcon --version
77
103
 
78
104
  Options:
79
- <config-file> Path to configuration file. Configuration file
80
- describes how new session is started. YAML format.
81
- <session-name> Name that identifies the session. Must be unique.
82
- <command> Command to be executed on remote server[s].
83
- <regex> String that represents valid Ruby regex.
84
- <index> 0-based index.
85
- <filter> Filter consisting of a valid ruby regex and
86
- optionally of a special predicate.
87
- For more information see README file.
88
- -s --ssh-config=file Path to ssh config file [default: ~/.ssh/config].
89
- -n --session-name=name Name of the session to be used in the tcon command.
90
- -p --purpose=description Description of session's purpose.
91
- --all Delete all existing sessions.
92
- -f --server-filter=filter Filter to select a subset of the servers via
93
- host names.
94
- -g --group-filter=regex Filter to select a subset of the servers via
95
- group membership.
96
- -r --filter=regex Filter to select a subset of the servers via
97
- host names or group membership.
98
- Combines --server-filter and --group-filter.
99
- -w --window=index Select a window via (0-based) index.
100
- -c --command-file=file File containing the list of commands to be
101
- executed on remote server[s].
102
- -v --verbose Report how many servers were affected by the send
103
- command.
104
- -h --help Show this screen.
105
- --version Show version.
105
+ <config-file> Path to configuration file. Configuration file
106
+ describes how new session is started. YAML.
107
+ <qs-args> Arguments needed to start a quick session.
108
+ <session-name> Name to identify the session. Must be unique.
109
+ <command> Command to be executed on remote server[s].
110
+ <regex> String that represents valid Ruby regex.
111
+ <index> 0-based index.
112
+ <filter> Filter consisting of a valid ruby regex and
113
+ optionally of a special predicate.
114
+ For more information see README file.
115
+ -q --quick-session=qs-args Start the seesion without a configuration file.
116
+ Specify necessary argumenst instead.
117
+ -s --ssh-config=file Path to ssh config file. [default: ~/.ssh/config]
118
+ -n --session-name=name Name of the session.
119
+ -p --purpose=description Description of session's purpose.
120
+ --all Delete all existing sessions.
121
+ -f --server-filter=filter Filter to select a subset of the servers via
122
+ host names.
123
+ -g --group-filter=regex Filter to select a subset of the servers via
124
+ group membership.
125
+ -r --filter=regex Filter to select a subset of the servers via
126
+ host names or group membership.
127
+ Combines --server-filter and --group-filter.
128
+ -w --window=index Select a window via (0-based) index.
129
+ -c --command-file=file File containing the list of commands to be
130
+ executed on remote server[s].
131
+ -v --verbose Report how many servers were affected by the
132
+ send command.
133
+ -h --help Show this screen.
134
+ --version Show version.
106
135
  ~~~
107
136
 
137
+
108
138
  ### Send command
109
139
 
110
140
  Send command sends user specified command(s) to chosen panes.
@@ -162,14 +192,10 @@ tcon send production -f 'worker' 'tail -f /var/log/syslog'
162
192
  tcon send production -f 'worker :: <,3]; 7; <9,13>' 'C-c'
163
193
  ~~~
164
194
 
165
- ## Configuration
166
195
 
167
- To use this gem, you need to create a configuration file. This shouldn't be
168
- that hard and here I provide exhaustive details about configuration files.
196
+ ## Configuration
169
197
 
170
- (If there is enough interest, in future versions there could be a special
171
- command to simplify generation of configuration files. To accelerate the
172
- process, open an issue or drop me an email: ivan@<< username >>.com)
198
+ To use this gem, you usually need to create a configuration file.
173
199
 
174
200
  Let's get to it.
175
201
 
@@ -248,6 +274,9 @@ hostless:
248
274
  merge-groups:
249
275
  misc: ['cache', 'db', 'mongodb']
250
276
  lbs: ['haproxy', 'nginx']
277
+ group-ranges:
278
+ cache: [1, 4]
279
+ node: [125, 131]
251
280
  multiple-hosts:
252
281
  regexes:
253
282
  - !ruby-regexp '(nginx|haproxy)-'
@@ -283,7 +312,7 @@ Host dev.database-staging-1
283
312
  ~~~
284
313
 
285
314
  and the following regex is used:
286
- ~~~
315
+ ~~~yaml
287
316
  regex: !ruby-regexp '^(\w+)\.(\w+)-([\w-]+)-(\d+)$'
288
317
  ~~~
289
318
 
@@ -345,7 +374,7 @@ Hostless panes can be used to:
345
374
  layout purposes) together. This can be used to group a few servers that are
346
375
  unique in type or small in numbers. E.g. grouping different DB servers.
347
376
 
348
- ~~~
377
+ ~~~yaml
349
378
  lbs: ['haproxy', 'nginx']
350
379
  ~~~
351
380
  In this example two different kinds of loadbalancers are grouped together.
@@ -355,6 +384,21 @@ original and merge-group name.
355
384
 
356
385
  Hostless groups can also be merged.
357
386
 
387
+ * * *
388
+ (optional) field __'group-ranges'__ contains groups that should be limited to
389
+ specific ranges of servers. Ranges are defined by lower and upper limits, both inclusive.
390
+ They are enforced on sort values (defined by aforementioned 'sort-by' field).
391
+
392
+ If the following group ranges were defined:
393
+ ~~~yaml
394
+ group-ranges:
395
+ cache: [1, 4]
396
+ node: [125, 131]
397
+ ~~~
398
+ Only cache servers with sort ids 1, 2, 3 or 4 would be included in the session.
399
+ Similarly for node servers, only servers with ids between 125 and 131 would be
400
+ included.
401
+
358
402
  * * *
359
403
  (optional) field __'multiple-hosts'__ contains __'regexes'__ and __'counts'__
360
404
  fields. With those, some hosts can have multiple connections established, not
@@ -401,6 +445,43 @@ Take a look at [`spec/fixtures/configs.yml`][configs] for some configuration
401
445
  possibilities (sections under 'input' fields).
402
446
 
403
447
 
448
+ ## Sessions without configuration files
449
+
450
+ As mentioned in previous section, normally you need the configuration file to
451
+ start the session.
452
+
453
+ This is not true for some smaller sessions where you can directly specify all
454
+ necessary configuration in command line when starting the session.
455
+
456
+ You can use `--quick-session` (or `-q`) to start a quick session. Quick
457
+ session arguments must conform to the following format:
458
+ ~~~
459
+ "<regex>[[<lower-limit>,<upper-limit>]]<additional-arguments>"
460
+ ~~~
461
+ where:
462
+
463
+ * `<regex>` is similar to 'regex' filed in configuration file, but without the
464
+ sorting part (which is presumably last part of the full regex)
465
+ * `<lower-limit>` and `<upper-limit>` are optional elements and define range of
466
+ valid servers. Range is enforced on sorting part (which is not part of
467
+ previous regex, but presumably comes after it)
468
+ * `<optional-additional-arguments>` is list of optional arguments. This list
469
+ starts with ' :: ' and consists of key-value pairs separated by semicolon.
470
+ Currently only `h` and `v` arguments are supported. Format:
471
+ `:: h <max-horizontal-panes>; v <max-vertical-panes>`
472
+
473
+ Examples:
474
+
475
+ - `tcon start -q "dev.node-staging-[[1,24]]"`
476
+ - `tcon start -q "dev.node-staging-[[42,]]"`
477
+ - `tcon start -q "dev.node-staging-[[,]]"`
478
+ - `tcon start -q "dev.node-staging-[[,42]] :: h 2; v 2"`
479
+
480
+ Example with more complex regex (selects 2 kinds of servers):
481
+
482
+ - `tcon start -q "dev.(nginx|haproxy)-staging-[[10,30]] :: h 2; v 2"`
483
+
484
+
404
485
  ## Requirements
405
486
  To be able to use the gem you should have ruby 1.9+ and tmux installed on a *nix
406
487
  (Mac OS X, Linux, ...) machine. (Windows: here be dragons)
@@ -463,6 +544,19 @@ user, and so configure tmux to use Vim-like bindings to switch panes. For more
463
544
  information, check my [dotfiles].
464
545
 
465
546
 
547
+ ### Sending the commands
548
+
549
+ You need to run the send command from somewhere. There are (at least) 2
550
+ options:
551
+
552
+ * have a controller pane (or the whole window) inside the tcon session
553
+ - a pane not connected to a server to issue the commands to other panes
554
+ * have a separate terminal pane for issuing the send commands
555
+ - I'm a iTerm2 user, and so I split the window vertically: much bigger
556
+ top pane for the tcon session, and a smaller bottom pane to issue the
557
+ send commands
558
+
559
+
466
560
  ## Contributing
467
561
 
468
562
  1. Fork it
@@ -480,9 +574,6 @@ comments (all other are welcome too). Just _drop me a line_. :)
480
574
 
481
575
  ## Comments, ideas or if you feel like chatting
482
576
 
483
- Take a look at `TODO.md` file (in the repository) for ideas about additional
484
- features in new versions.
485
-
486
577
  ivan@<< username >>.com
487
578
 
488
579
  I'd be happy to hear from you.
@@ -490,6 +581,7 @@ I'd be happy to hear from you.
490
581
  Also, visit my [homepage].
491
582
 
492
583
 
584
+ [blog-post]: http://www.ikusalic.com/blog/2013/06/18/managing-multiple-servers-with-tcon/
493
585
  [configs]: /spec/fixtures/configs.yml
494
586
  [docopt]: https://github.com/docopt/docopt
495
587
  [tmux]: http://en.wikipedia.org/wiki/Tmux
@@ -12,12 +12,13 @@ even after computer restarts. Complex sessions with different layouts for
12
12
  different kinds of servers can be easily created.
13
13
 
14
14
  Usage:
15
- tcon start <config-file> [--ssh-config=<file>]
16
- [--session-name=<name>] [--purpose=<description>]
15
+ tcon start ( <config-file> | --quick-session=<qs-args> )
16
+ [--ssh-config=<file>] [--session-name=<name>]
17
+ [--purpose=<description>]
17
18
  tcon resume <session-name>
18
19
  tcon delete (<session-name> | --all)
19
20
  tcon list
20
- tcon send <session-name> (<command> | --command-file=<file>)
21
+ tcon send <session-name> ( <command> | --command-file=<file> )
21
22
  [ --server-filter=<filter> | --group-filter=<regex>
22
23
  | --filter=<regex> | --window=<index> ]
23
24
  [--verbose]
@@ -25,33 +26,36 @@ Usage:
25
26
  tcon --version
26
27
 
27
28
  Options:
28
- <config-file> Path to configuration file. Configuration file
29
- describes how new session is started. YAML format.
30
- <session-name> Name that identifies the session. Must be unique.
31
- <command> Command to be executed on remote server[s].
32
- <regex> String that represents valid Ruby regex.
33
- <index> 0-based index.
34
- <filter> Filter consisting of a valid ruby regex and
35
- optionally of a special predicate.
36
- For more information see README file.
37
- -s --ssh-config=file Path to ssh config file [default: ~/.ssh/config].
38
- -n --session-name=name Name of the session to be used in the tcon command.
39
- -p --purpose=description Description of session's purpose.
40
- --all Delete all existing sessions.
41
- -f --server-filter=filter Filter to select a subset of the servers via
42
- host names.
43
- -g --group-filter=regex Filter to select a subset of the servers via
44
- group membership.
45
- -r --filter=regex Filter to select a subset of the servers via
46
- host names or group membership.
47
- Combines --server-filter and --group-filter.
48
- -w --window=index Select a window via (0-based) index.
49
- -c --command-file=file File containing the list of commands to be
50
- executed on remote server[s].
51
- -v --verbose Report how many servers were affected by the send
52
- command.
53
- -h --help Show this screen.
54
- --version Show version.
29
+ <config-file> Path to configuration file. Configuration file
30
+ describes how new session is started. YAML.
31
+ <qs-args> Arguments needed to start a quick session.
32
+ <session-name> Name to identify the session. Must be unique.
33
+ <command> Command to be executed on remote server[s].
34
+ <regex> String that represents valid Ruby regex.
35
+ <index> 0-based index.
36
+ <filter> Filter consisting of a valid ruby regex and
37
+ optionally of a special predicate.
38
+ For more information see README file.
39
+ -q --quick-session=qs-args Start the seesion without a configuration file.
40
+ Specify necessary argumenst instead.
41
+ -s --ssh-config=file Path to ssh config file. [default: ~/.ssh/config]
42
+ -n --session-name=name Name of the session.
43
+ -p --purpose=description Description of session's purpose.
44
+ --all Delete all existing sessions.
45
+ -f --server-filter=filter Filter to select a subset of the servers via
46
+ host names.
47
+ -g --group-filter=regex Filter to select a subset of the servers via
48
+ group membership.
49
+ -r --filter=regex Filter to select a subset of the servers via
50
+ host names or group membership.
51
+ Combines --server-filter and --group-filter.
52
+ -w --window=index Select a window via (0-based) index.
53
+ -c --command-file=file File containing the list of commands to be
54
+ executed on remote server[s].
55
+ -v --verbose Report how many servers were affected by the
56
+ send command.
57
+ -h --help Show this screen.
58
+ --version Show version.
55
59
  HERE
56
60
 
57
61
  def self.main(input_args)
@@ -13,14 +13,18 @@ module TmuxConnector
13
13
  attr_reader :session
14
14
 
15
15
  def initialize(args)
16
- @config = TmuxConnector.get_config args['<config-file>']
16
+ if args['<config-file>']
17
+ @config = TmuxConnector.get_config args['<config-file>']
18
+ elsif args['--quick-session']
19
+ @config = quick_session_to_config args['--quick-session']
20
+ end
17
21
 
18
22
  ssh_hostnames = SSHConfig.get_hosts(args['--ssh-config'], config['reject-regex'])
19
23
  @hosts = ssh_hostnames.reduce([]) do |acc, name|
20
24
  ( acc << Host.new(name, config) ) rescue nil
21
25
  acc
22
26
  end
23
- raise "no hosts matching given configuration found, check your configuration file" if hosts.empty?
27
+ raise "no hosts matching given configuration found, check your configuration" if hosts.empty?
24
28
 
25
29
  generate_groups
26
30
  generate_merge_rules
@@ -35,6 +39,62 @@ module TmuxConnector
35
39
 
36
40
  private
37
41
 
42
+ def quick_session_to_config(quick_args)
43
+ quick_regex = /(?<regex>.+)\[\[(?<first>[^,]*),\s*(?<second>[^\]]*)\]\]( :: (?<args>.+))?/
44
+ str_regex, first, last, additiona_args = quick_args.match(quick_regex)[1 .. -1]
45
+
46
+ first = "" if first =~ /^\s*$/
47
+ second = "" if second =~ /^\s*$/
48
+
49
+ conf = generate_fake_config str_regex
50
+ TmuxConnector.process_config! conf
51
+ apply_additional_arguments! conf, additiona_args
52
+
53
+ conf['group-ranges'] = {
54
+ TmuxConnector::QUICK_GROUP_ID => [first, last]
55
+ }
56
+
57
+ return conf
58
+ rescue
59
+ raise 'quick session argument parsing failed'
60
+ end
61
+
62
+ def generate_fake_config(str_regex)
63
+ return {
64
+ 'regex' => "(#{ str_regex })(.+)",
65
+ 'regex-parts-to' => {
66
+ 'group-by' => [0],
67
+ 'sort-by' => [-1]
68
+ }
69
+ }
70
+ end
71
+
72
+ def apply_additional_arguments!(conf, args)
73
+ return if args.nil?
74
+
75
+ args_delimiter = /;\s*/
76
+
77
+ options = args.split(args_delimiter).reduce({}) do |acc, e|
78
+ k, v = e.split
79
+ acc[k] = v
80
+ acc
81
+ end
82
+
83
+ begin
84
+ h, v = Integer(options['h'], 10), Integer(options['v'], 10)
85
+ conf['layout'] = {
86
+ 'default' => {
87
+ 'custom' => {
88
+ 'max-horizontal' => h,
89
+ 'max-vertical' => v,
90
+ 'panes-flow' => 'horizontal'
91
+ }
92
+ }
93
+ }
94
+ rescue
95
+ end
96
+ end
97
+
38
98
  def generate_groups()
39
99
  @groups = hosts.reduce({}) do |acc, e|
40
100
  acc[e.group_id] ||= []
@@ -1,4 +1,6 @@
1
1
  module TmuxConnector
2
+ QUICK_GROUP_ID = 'quick_group_id'
3
+
2
4
  class Host
3
5
  attr_accessor :sort_value
4
6
 
@@ -15,6 +17,7 @@ module TmuxConnector
15
17
  @sort_value = config['regex-parts-to']['sort-by'].map { |i| groups[i] }.join '-'
16
18
  @group_id = config['regex-parts-to']['group-by'].map { |i| groups[i] }.join '-'
17
19
 
20
+ check_range! config['group-ranges']
18
21
  @count = get_count config
19
22
  end
20
23
 
@@ -24,6 +27,22 @@ module TmuxConnector
24
27
 
25
28
  private
26
29
 
30
+ def check_range!(ranges)
31
+ return if ranges.nil?
32
+
33
+ range = ranges[group_id] || ranges[TmuxConnector::QUICK_GROUP_ID]
34
+ if range
35
+ a, b = range.map(&:to_s)
36
+ x = sort_value
37
+
38
+ numbers_only = [a, b, x].all? { |e| e =~ /^[-+]?[0-9]*$/ }
39
+ a, b, x = [a, b, x].map { |e| Integer(e, 10) rescue nil } if numbers_only
40
+
41
+ raise if a && x < a
42
+ raise if b && x > b
43
+ end
44
+ end
45
+
27
46
  def create_display_name(groups, config)
28
47
  if config['name']
29
48
  parts = []
@@ -1,3 +1,3 @@
1
1
  module TmuxConnector
2
- VERSION = "0.9.7"
2
+ VERSION = "1.0.8"
3
3
  end
@@ -55,5 +55,9 @@ describe "Configuration file" do
55
55
  it_should_behave_like "config test", 'hostless'
56
56
  it_should_behave_like "config test", 'hostless-merge'
57
57
  end
58
+
59
+ describe "group intervals" do
60
+ it_should_behave_like "config test", 'group-intervals'
61
+ end
58
62
  end
59
63
  end
@@ -48,6 +48,18 @@ merge:
48
48
  misc: ['repo', 'mongodb', 'sshforwarder']
49
49
  lbs: ['loadbalancer', 'ngx']
50
50
 
51
+ group-intervals:
52
+ input:
53
+ <<: *input-min
54
+ group-intervals:
55
+ lxc: [199, 243]
56
+ ngx: [11, 42]
57
+ expected:
58
+ <<: *expected-min
59
+ group-intervals:
60
+ lxc: [199, 243]
61
+ ngx: [11, 42]
62
+
51
63
  layout-default:
52
64
  input:
53
65
  <<: *input-min
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tmux-connector
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.7
4
+ version: 1.0.8
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-06-17 00:00:00.000000000 Z
12
+ date: 2013-07-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: docopt
@@ -60,7 +60,6 @@ files:
60
60
  - LICENSE.txt
61
61
  - README.md
62
62
  - Rakefile
63
- - TODO.md
64
63
  - bin/tcon
65
64
  - lib/tmux-connector.rb
66
65
  - lib/tmux-connector/command_handler.rb
data/TODO.md DELETED
@@ -1,12 +0,0 @@
1
- possible features for new version:
2
- * ensure it works with zsh
3
- * generate default config via just regex(es)
4
- * add strict config file validation
5
- * add window (via option) that won't connect to nay server
6
- - for tcon commands and regular actions on local machine
7
- * startup command
8
-
9
- code related:
10
- * add specs
11
- * test with ruby 2.0
12
- * refactoring