rutty 2.1.1 → 2.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,23 +1,79 @@
1
1
  RuTTY
2
2
  =====
3
3
 
4
- RuTTY is a DSH implementation in Ruby.
4
+ RuTTY is a DSH implementation in Ruby. You can use it to execute shell commands on multiple remote
5
+ servers simultaneously using a tagging system to target just the servers you want.
6
+
7
+ Also supports SCP uploads to multiple remote servers using the same tagging system.
5
8
 
6
9
  Requirements
7
10
  ------------
8
11
 
9
12
  * Ruby >= 1.8.7 (Not tested on 1.9.x)
10
13
  * Rubygems >= 1.3.7
14
+
15
+ ###Development Requirements###
16
+
11
17
  * Bundler >= 1.0.0
12
18
 
13
19
  Installation
14
20
  ------------
15
21
 
16
- $ sudo gem install bundler
17
- $ git clone git://github.com/jlindsey/rutty.git
18
- $ cd rutty
19
- $ bundle install
20
- $ ./rutty --help
22
+ $ sudo gem install rutty
23
+ $ rutty init
24
+ $ rutty help
25
+
26
+ Usage
27
+ -----
28
+
29
+ ###Init###
30
+
31
+ You must first initialize the RuTTY configuration and data directory with the `rutty init` command. This
32
+ command takes an optional argument to specify the directory to install into. If omitted, it will install
33
+ into `~/.rutty/`. Note that if you install into a directory other than the default, you will have to supply
34
+ the config to all the other commands with the `-c` option.
35
+
36
+ $ rutty init
37
+ create /Users/jlindsey/.rutty
38
+ create /Users/jlindsey/.rutty/defaults.yaml
39
+ create /Users/jlindsey/.rutty/nodes.yaml
40
+
41
+
42
+ ###Adding Nodes###
43
+
44
+ After initialization, you must add nodes to the RuTTY config. This is done with the `rutty add_node` command.
45
+ Invoking `rutty help add_node` will give you a list of all the options to pass into it. Any options you don't pass
46
+ will be filled in from the defaults at `$RUTTY_HOME/defaults.yaml`.
47
+
48
+ $ rutty add_node example.com -u root -k /Users/jlindsey/.ssh/id_rsa --tags example,test
49
+
50
+ The above will add a node to the RuTTY config that looks like this (in YAML):
51
+
52
+ ---
53
+ host: example.com
54
+ user: root
55
+ keypath: /Users/jlindsey/.ssh/id_rsa
56
+ tags:
57
+ - example
58
+ - test
59
+ port: 22
60
+
61
+ Note that the `port: 22` line was filled in from the defaults because it was not specified.
62
+
63
+ ###Running Commands###
64
+
65
+ Now that we have a node, we can run commands on it. The default RuTTY command is the `dsh` action, so it
66
+ can be omitted. That is to say, the following two commands are identical:
67
+
68
+ $ rutty dsh -a uptime
69
+ $ rutty -a uptime
70
+
71
+ The `dsh` action can accept either a list of tags passed via `--tags` or the `-a` flag, which will run the command
72
+ on all defined nodes regardless of tags.
73
+
74
+ Note that any command that has any whitespace in it must be enclosed in quotes.
75
+
76
+ $ rutty -a "free -m"
21
77
 
22
78
  TODO
23
79
  ----
@@ -28,7 +84,6 @@ TODO
28
84
  * Implement delete_node command
29
85
  * Make better printouts
30
86
  * Documentation
31
- * Tests
32
87
 
33
88
  Copyright
34
89
  ---------
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.1.1
1
+ 2.1.2
@@ -7,7 +7,19 @@ require 'rutty/helpers'
7
7
  require 'rutty/node'
8
8
  require 'rutty/nodes'
9
9
 
10
+ ##
11
+ # The RuTTY top-level module. Everything in the RuTTY gem is contained in this module.
12
+ #
13
+ # @author Josh Lindsey
14
+ # @since 2.0.0
10
15
  module Rutty
16
+
17
+ ##
18
+ # The Rutty::Runner class includes mixins from the other modules. All end-user interaction
19
+ # should be done through this class.
20
+ #
21
+ # @author Josh Lindsey
22
+ # @since 2.0.0
11
23
  class Runner
12
24
  attr_writer :config_dir
13
25
 
@@ -15,18 +27,37 @@ module Rutty
15
27
  include Rutty::Helpers
16
28
  include Rutty::Actions
17
29
 
30
+ ##
31
+ # Initialize a new {Rutty::Runner} instance
32
+ #
33
+ # @param config_dir [String] Optional parameter specifying the directory RuTTY has been init'd into
18
34
  def initialize config_dir = nil
19
35
  self.config_dir = config_dir
20
36
  end
21
37
 
38
+ ##
39
+ # Lazy-load the {Rutty::Config} object for this instance, based on the config_dir param
40
+ # passed to {#initialize}.
41
+ #
42
+ # @return [Rutty::Config]
22
43
  def config
23
44
  @config ||= Rutty::Config.load_config self.config_dir
24
45
  end
25
46
 
47
+ ##
48
+ # Lazy-load the {Rutty::Nodes} object containing the user-defined nodes' connection info.
49
+ # Loads from the config_dir param passed to {#initialize}
50
+ #
51
+ # @return [Rutty::Nodes]
26
52
  def nodes
27
53
  @nodes ||= Rutty::Nodes.load_config self.config_dir
28
54
  end
29
55
 
56
+ ##
57
+ # If @config_dir is nil, returns {Rutty::Consts::CONF_DIR}. Otherwise return @config_dir.
58
+ #
59
+ # @see Rutty::Consts::CONF_DIR
60
+ # @return [String] The user-specified config directory, falling back to the default on nil
30
61
  def config_dir
31
62
  (@config_dir.nil? && Rutty::Consts::CONF_DIR) || @config_dir
32
63
  end
@@ -1,7 +1,20 @@
1
1
  require 'rutty/consts'
2
2
 
3
3
  module Rutty
4
+
5
+ ##
6
+ # The primary mixin module containing the code executed by the rutty bin's actions.
7
+ #
8
+ # @author Josh Lindsey
9
+ # @since 2.0.0
4
10
  module Actions
11
+ ##
12
+ # Initialize the Rutty config file structure in the specified directory, or
13
+ # report that the files already exist there.
14
+ #
15
+ # @see Rutty::Runner#config_dir
16
+ # @param [String] dir The directory to install into. {Rutty::Runner#config_dir} ensures
17
+ # that this falls back to {Rutty::Consts::CONF_DIR} if not passed in by the user.
5
18
  def init dir
6
19
  general_file = File.join(dir, Rutty::Consts::GENERAL_CONF_FILE)
7
20
  nodes_file = File.join(dir, Rutty::Consts::NODES_CONF_FILE)
@@ -40,6 +53,12 @@ module Rutty
40
53
  end
41
54
  end
42
55
 
56
+ ##
57
+ # Add a new user-defined node to the datastore file.
58
+ #
59
+ # @see http://visionmedia.github.com/commander/
60
+ # @param [Array] args ARGV passed by the bin
61
+ # @param [Object] options The parsed options object as passed by the bin
43
62
  def add_node args, options
44
63
  raise Rutty::BadUsage.new "Must supply a hostname or IP address.
45
64
  See `rutty help add_node' for usage" if args.empty?
@@ -54,12 +73,23 @@ module Rutty
54
73
  self.nodes.write_config self.config_dir
55
74
  end
56
75
 
76
+ ##
77
+ # List all the user-defined nodes currently stored in the datastore file.
78
+ #
79
+ # @see (see #add_node)
80
+ # @param (see #add_node)
57
81
  def list_nodes args, options
58
82
  require 'pp'
59
83
 
60
84
  pp self.nodes.filter(options)
61
85
  end
62
86
 
87
+ ##
88
+ # Cycle through all the user-defined nodes, filtered by the options, connect to them
89
+ # and run the specified command on them.
90
+ #
91
+ # @see (see #add_node)
92
+ # @param (see #add_node)
63
93
  def dsh args, options
64
94
  # TODO: Clean this up, it's pretty hard to read and follow
65
95
 
@@ -138,6 +168,12 @@ module Rutty
138
168
  pp @returns
139
169
  end
140
170
 
171
+ ##
172
+ # Cycle through all the user-defined nodes, filtered by the options, connect to them
173
+ # and upload the specified file(s).
174
+ #
175
+ # @see (see #add_node)
176
+ # @param (see #add_node)
141
177
  def scp args, options
142
178
  check_installed!
143
179
  raise Rutty::BadUsage.new "Must supply a local path and a remote path" unless args.length == 2
@@ -1,7 +1,29 @@
1
- # http://mjijackson.com/2010/02/flexible-ruby-config-objects
2
1
  module Rutty
2
+
3
+ ##
4
+ # Flexible config class able to use both hash accessors and object accessors. Nested
5
+ # attributes are also instances of this class, allowing for object (dot) style accessor
6
+ # chaining.
7
+ #
8
+ # @see http://mjijackson.com/2010/02/flexible-ruby-config-objects
9
+ #
10
+ # @example
11
+ # config = Config.new :foo => "bar", :test => "baz", :widget => { :another => "string" }
12
+ #
13
+ # config.foo # => "bar"
14
+ # config.widget.another # => "string"
15
+ #
16
+ # @author Michael Jackson
17
+ # @author Josh Lindsey
18
+ # @since 2.0.0
3
19
  class Config
4
20
  class << self
21
+ ##
22
+ # Loads the default config data from the yaml file in the specified config dir.
23
+ #
24
+ # @see Rutty::Consts::GENERAL_CONF_FILE
25
+ # @param [String] dir The directory to look in for the file specified by {Rutty::Consts::GENERAL_CONF_FILE}
26
+ # @return [Rutty::Config] The populated {Config} instance
5
27
  def load_config dir
6
28
  require 'yaml'
7
29
 
@@ -10,21 +32,41 @@ module Rutty
10
32
  end
11
33
  end
12
34
 
35
+ ##
36
+ # Returns a new {Config} populated with the specified data hash.
37
+ #
38
+ # @see #update!
39
+ # @param (see #update!)
13
40
  def initialize(data={})
14
41
  @data = {}
15
42
  update!(data)
16
43
  end
17
44
 
45
+ ##
46
+ # Updates this instance's @data attribute with the specified data hash.
47
+ #
48
+ # @param [Hash] data Data hash to iterate over
49
+ # @see #[]=
18
50
  def update!(data)
19
51
  data.each do |key, value|
20
52
  self[key] = value
21
53
  end
22
54
  end
23
55
 
56
+ ##
57
+ # Retrieve the specified key via Hash accessor syntax.
58
+ #
59
+ # @param [Symbol, String] key The key to lookup
60
+ # @return [Object] The object corresponding to the key
24
61
  def [](key)
25
62
  @data[key.to_sym]
26
63
  end
27
64
 
65
+ ##
66
+ # Set the specified key as the specified value via Hash accessor syntax.
67
+ #
68
+ # @param [Symbol, String] key The key to set
69
+ # @param [Object] value The value to set into the key
28
70
  def []=(key, value)
29
71
  if value.class == Hash
30
72
  @data[key.to_sym] = Config.new(value)
@@ -33,10 +75,20 @@ module Rutty
33
75
  end
34
76
  end
35
77
 
78
+ ##
79
+ # Simply returns this instance's @data attribute, which is internally stored as a hash.
80
+ #
81
+ # @return [Hash] The @data attribute
36
82
  def to_hash
37
83
  @data
38
84
  end
39
85
 
86
+ ##
87
+ # Allows for object accessor (dot) syntax to access the stored data.
88
+ # If the missing method ends with an equals, calls {#[]=}, otherwise calls {#[]}
89
+ #
90
+ # @param [Symbol] sym The method symbol
91
+ # @param [*Array] args The splatted array of method arguments
40
92
  def method_missing(sym, *args)
41
93
  if sym.to_s =~ /(.+)=$/
42
94
  self[$1] = args.first
@@ -1,10 +1,20 @@
1
1
  module Rutty
2
+ ##
3
+ # Simple container module for constants.
4
+ #
5
+ # @author Josh Lindsey
6
+ # @since 2.0.0
2
7
  module Consts
8
+ ## Name of the general (defaults) config file
3
9
  GENERAL_CONF_FILE = 'defaults.yaml'
10
+ ## Name of the datastore file for user-defined nodes
4
11
  NODES_CONF_FILE = 'nodes.yaml'
5
12
 
13
+ ## Default configuaration storage directory
6
14
  CONF_DIR = File.join(ENV['HOME'], '.rutty')
15
+ ## Default general (defaults) config file location
7
16
  GENERAL_CONF = File.join(CONF_DIR, GENERAL_CONF_FILE)
17
+ ## Default nodes datastore file location
8
18
  NODES_CONF = File.join(CONF_DIR, NODES_CONF_FILE)
9
19
  end
10
20
  end
@@ -1,5 +1,15 @@
1
1
  module Rutty
2
+ ##
3
+ # Raised by {Rutty::Helpers#check_installed!} if the check fails.
4
+ #
5
+ # @author Josh Lindsey
6
+ # @since 2.0.0
2
7
  class NotInstalledError < StandardError; end
3
8
 
9
+ ##
10
+ # Raised by various {Rutty::Actions} methods on invalid options, etc.
11
+ #
12
+ # @author Josh Lindsey
13
+ # @since 2.0.0
4
14
  class BadUsage < StandardError; end
5
15
  end
@@ -1,9 +1,19 @@
1
1
  require 'rutty/errors'
2
- require 'rutty/consts'
3
2
  require 'rutty/version'
4
3
 
5
4
  module Rutty
5
+
6
+ ##
7
+ # Simple mixin module for miscellaneous methods that don't fit in elsewhere.
8
+ #
9
+ # @author Josh Lindsey
10
+ # @since 2.0.0
6
11
  module Helpers
12
+ ##
13
+ # Check to ensure the config dir exists. Method expects this module to be included in a
14
+ # class or module where self.config_dir is meaningful, such as {Rutty::Runner}.
15
+ #
16
+ # @raise [Rutty::NotInstalledError] If file cannot be found
7
17
  def check_installed!
8
18
  unless File.exists? self.config_dir
9
19
  raise Rutty::NotInstalledError.new %Q(Can't find conf directory at #{self.config_dir}.
@@ -11,6 +21,11 @@ module Rutty
11
21
  end
12
22
  end
13
23
 
24
+ ##
25
+ # Returns the version string contained in {Rutty::Version::STRING}. Used by the rutty bin.
26
+ #
27
+ # @see (see Rutty::Version)
28
+ # @return [String] The version string
14
29
  def self.get_version
15
30
  Rutty::Version::STRING
16
31
  end
@@ -1,21 +1,51 @@
1
1
  require 'rutty/config'
2
- require 'rutty/consts'
3
2
 
4
3
  module Rutty
5
- class Node < Config
4
+
5
+ ##
6
+ # A wrapper class representing an individual node. Normally contained by {Rutty::Nodes}.
7
+ #
8
+ # @see Rutty::Config
9
+ # @author Josh Lindsey
10
+ # @since 2.0.0
11
+ class Node < Config
12
+ ##
13
+ # Initialize a new {Rutty::Node} instance by merging the user-provided data with
14
+ # the configured defaults.
15
+ #
16
+ # @see Rutty::Config#initialize
17
+ # @param [Hash] data The data provided by the user
18
+ # @param [Hash] defaults The defaults data provided by the {Rutty::Config} class
6
19
  def initialize data, defaults = {}
7
20
  merged_data = defaults.merge data
8
21
  super merged_data
9
22
  end
10
23
 
24
+ ##
25
+ # Whether this object's tags array includes the specified tag.
26
+ #
27
+ # @param [String] tag The tag string to check for
11
28
  def has_tag? tag
12
29
  self.tags.include? tag
13
30
  end
14
31
 
32
+ ##
33
+ # Relation of this {Rutty::Node} to another {Rutty::Node}. Used for sorting.
34
+ # Compares the host property of the nodes, as it's the only property guaranteed to be
35
+ # set and unique.
36
+ #
37
+ # @see http://ruby-doc.org/core/classes/String.html#M000763
38
+ # @param [Rutty::Node] other The other {Rutty::Node} to compare to
39
+ # @return [Integer] Relation of self to other: -1 for less-than, 0 for equal-to, 1 for greater-than
15
40
  def <=> other
16
41
  self.host <=> other.host
17
42
  end
18
43
 
44
+ ##
45
+ # Checks for object eqality. Checks each property of this {Node} against the other.
46
+ #
47
+ # @param (see Rutty::Node#<=>)
48
+ # @return [Boolean]
19
49
  def == other
20
50
  self.host == other.host and
21
51
  self.port == other.port and
@@ -2,14 +2,36 @@ require 'rutty/node'
2
2
  require 'rutty/consts'
3
3
 
4
4
  module Rutty
5
+
6
+ ##
7
+ # Simple container class for {Rutty::Node} instances. Contains methods to load node data from file,
8
+ # write data back to the file, and filter nodes based on user-supplied criteria.
9
+ #
10
+ # @author Josh Lindsey
11
+ # @since 2.1.0
5
12
  class Nodes < Array
6
13
  class << self
14
+ ##
15
+ # Loads the users's node data from the yaml file contained in the specified dir.
16
+ #
17
+ # @param [String] dir The directory to look in for the filename specified by {Rutty::Consts::NODES_CONF_FILE}
18
+ # @return [Rutty::Nodes] The filled instance of {Rutty::Node} objects
19
+ # @see Rutty::Consts::NODES_CONF_FILE
7
20
  def load_config dir
8
21
  require 'yaml'
9
22
  Rutty::Nodes.new YAML.load(File.open(File.join(dir, Rutty::Consts::NODES_CONF_FILE)).read)
10
23
  end
11
24
  end
12
25
 
26
+ ##
27
+ # Filters out the {Rutty::Node} objects contained in this instance based on user-defined criteria.
28
+ #
29
+ # @param [Hash, #[]] opts The filter criteria
30
+ # @option opts [String] :keypath (nil) The path to the private key
31
+ # @option opts [String] :user (nil) The user to login as
32
+ # @option opts [Integer] :port (nil) The port to connect to
33
+ # @option opts [Array<String>] :tags (nil) The tags to filter by
34
+ # @return [Array] An Array containing the {Rutty::Node} objects after filtering
13
35
  def filter opts = {}
14
36
  return self if opts[:all]
15
37
 
@@ -23,6 +45,11 @@ module Rutty
23
45
  ary
24
46
  end
25
47
 
48
+ ##
49
+ # Writes the updated nodes config back into the nodes.yaml file in the specified dir.
50
+ #
51
+ # @param (see Rutty::Nodes.load_config)
52
+ # @see (see Rutty::Nodes.load_config)
26
53
  def write_config dir
27
54
  File.open(File.join(dir, Rutty::Consts::NODES_CONF_FILE), 'w') do |f|
28
55
  YAML.dump(self, f)
@@ -1,8 +1,15 @@
1
1
  module Rutty
2
+
3
+ ##
4
+ # The current version of the gem, expressed in Ruby code so it can
5
+ # be accessed programmatically.
6
+ #
7
+ # @author Josh Lindsey
8
+ # @see http://semver.org
2
9
  module Version
3
10
  MAJOR = 2
4
11
  MINOR = 1
5
- PATCH = 1
12
+ PATCH = 2
6
13
  BUILD = nil
7
14
 
8
15
  STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{rutty}
8
- s.version = "2.1.1"
8
+ s.version = "2.1.2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Josh Lindsey"]
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rutty
3
3
  version: !ruby/object:Gem::Version
4
- hash: 9
4
+ hash: 15
5
5
  prerelease: false
6
6
  segments:
7
7
  - 2
8
8
  - 1
9
- - 1
10
- version: 2.1.1
9
+ - 2
10
+ version: 2.1.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Josh Lindsey