rutty 2.1.1 → 2.1.2

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