marionetta 0.4.1 → 0.4.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 +24 -3
- data/lib/marionetta/command_runner.rb +52 -23
- data/lib/marionetta/group.rb +52 -1
- data/lib/marionetta/manipulators/puppet_manipulator.rb +1 -1
- data/lib/marionetta/manipulators.rb +18 -0
- data/lib/marionetta/rake_helper.rb +6 -0
- data/lib/marionetta.rb +5 -4
- data/marionetta.gemspec +4 -0
- metadata +2 -2
    
        data/README.md
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            # Marionetta
         | 
| 2 2 |  | 
| 3 | 
            -
            Marionetta is a ruby library for executing | 
| 4 | 
            -
            or more remote machines via SSH.
         | 
| 3 | 
            +
            [Marionetta][marionetta] is a ruby library for executing
         | 
| 4 | 
            +
            commands on one or more remote machines via SSH.
         | 
| 5 5 |  | 
| 6 6 | 
             
            It provides puppet provisioning without the need for a puppet
         | 
| 7 7 | 
             
            master and can also deploy your application code (with
         | 
| @@ -22,6 +22,21 @@ source 'http://rubygems.org' | |
| 22 22 | 
             
            gem 'marionetta'
         | 
| 23 23 | 
             
            ```
         | 
| 24 24 |  | 
| 25 | 
            +
            [marionetta]: http://drpheltright.github.com/marionetta/
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            ## Documentation
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            Marionetta has [annotated source][docs] that provides the
         | 
| 30 | 
            +
            bulk of documentation for Marionetta. Hopefully you'll find
         | 
| 31 | 
            +
            the annotations informative on *how to use* this library. If
         | 
| 32 | 
            +
            you feel they could be improved please create an issue on
         | 
| 33 | 
            +
            GitHub.
         | 
| 34 | 
            +
             | 
| 35 | 
            +
            If you prefer looking at the code, check it out on [github][github].
         | 
| 36 | 
            +
             | 
| 37 | 
            +
            [docs]: http://drpheltright.github.com/marionetta/docs/marionetta.html
         | 
| 38 | 
            +
            [github]: http://github.com/DrPheltRight/marionetta/
         | 
| 39 | 
            +
             | 
| 25 40 | 
             
            ## Using Marionetta in your Rakefile
         | 
| 26 41 |  | 
| 27 42 | 
             
            Marionetta provides an easy mechanism to generate rake tasks
         | 
| @@ -153,6 +168,12 @@ staging.manipulate_each_server(:deployer, :rollback) | |
| 153 168 |  | 
| 154 169 | 
             
            Luke Morton a.k.a. DrPheltRight
         | 
| 155 170 |  | 
| 171 | 
            +
            ## Collaborating
         | 
| 172 | 
            +
             | 
| 173 | 
            +
            Create an issue and send a pull request if you get any bright
         | 
| 174 | 
            +
            ideas or have a fix. Feel free to create an issue and not send
         | 
| 175 | 
            +
            one too, feedback is always welcome.
         | 
| 176 | 
            +
             | 
| 156 177 | 
             
            ## License
         | 
| 157 178 |  | 
| 158 | 
            -
            MIT
         | 
| 179 | 
            +
            Licensed under MIT by Luke Morton, 2012.
         | 
| @@ -27,9 +27,15 @@ module Marionetta | |
| 27 27 |  | 
| 28 28 | 
             
                ### Local execution
         | 
| 29 29 |  | 
| 30 | 
            -
                # Local commands are executed with `.system()`.  | 
| 31 | 
            -
                #  | 
| 32 | 
            -
                #  | 
| 30 | 
            +
                # Local commands are executed with `.system()`. We use
         | 
| 31 | 
            +
                # `Open4::popen4` to capture the output of the command run
         | 
| 32 | 
            +
                # neatly.
         | 
| 33 | 
            +
                # 
         | 
| 34 | 
            +
                # The command run is logged as info, output as debug and
         | 
| 35 | 
            +
                # any exceptions thrown are sent as fatal.
         | 
| 36 | 
            +
                # 
         | 
| 37 | 
            +
                # You can optionally pass in a block which receives
         | 
| 38 | 
            +
                # `stdout` and `stderr` as arguments:
         | 
| 33 39 | 
             
                # 
         | 
| 34 40 | 
             
                #     cmd.system('ls ~') do |out, err|
         | 
| 35 41 | 
             
                #       puts out
         | 
| @@ -57,6 +63,28 @@ module Marionetta | |
| 57 63 | 
             
                  return status.exitstatus == 0
         | 
| 58 64 | 
             
                end
         | 
| 59 65 |  | 
| 66 | 
            +
                # Create an archive of a local directory, optionally
         | 
| 67 | 
            +
                # saving it to a directory or file path.
         | 
| 68 | 
            +
                # 
         | 
| 69 | 
            +
                def archive(directory, save_to = nil)
         | 
| 70 | 
            +
                  if save_to.nil?
         | 
| 71 | 
            +
                    save_to = "#{directory}.#{server[:archive][:ext]}"
         | 
| 72 | 
            +
                  elsif File.directory?(save_to)
         | 
| 73 | 
            +
                    dirname = File.basename(directory)
         | 
| 74 | 
            +
                    save_to = "#{save_to}/#{dirname}.#{server[:archive][:ext]}"
         | 
| 75 | 
            +
                  end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                  archive_cmd = [
         | 
| 78 | 
            +
                    server[:archive][:command],
         | 
| 79 | 
            +
                    server[:archive][:flags],
         | 
| 80 | 
            +
                    ['-f', save_to],
         | 
| 81 | 
            +
                    ['-C', File.dirname(directory)],
         | 
| 82 | 
            +
                    File.basename(directory),
         | 
| 83 | 
            +
                  ]
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                  system(*archive_cmd.flatten)
         | 
| 86 | 
            +
                end
         | 
| 87 | 
            +
             | 
| 60 88 | 
             
                # The last command run by `.system()` is accessible via
         | 
| 61 89 | 
             
                # the `.last` attribute.
         | 
| 62 90 | 
             
                # 
         | 
| @@ -72,7 +100,7 @@ module Marionetta | |
| 72 100 | 
             
                # A block can be called against this method just like
         | 
| 73 101 | 
             
                # `.system()` in order to get `stdout` and `stderr`.
         | 
| 74 102 | 
             
                # 
         | 
| 75 | 
            -
                #  | 
| 103 | 
            +
                # Example:
         | 
| 76 104 | 
             
                # 
         | 
| 77 105 | 
             
                #     server = Marionetta.default_server
         | 
| 78 106 | 
             
                #     server[:hostname] = 'example.com'
         | 
| @@ -96,25 +124,9 @@ module Marionetta | |
| 96 124 | 
             
                  system(*ssh_cmd.flatten, &block)
         | 
| 97 125 | 
             
                end
         | 
| 98 126 |  | 
| 99 | 
            -
                 | 
| 100 | 
            -
             | 
| 101 | 
            -
             | 
| 102 | 
            -
                  elsif File.directory?(save_to)
         | 
| 103 | 
            -
                    dirname = File.basename(directory)
         | 
| 104 | 
            -
                    save_to = "#{save_to}/#{dirname}.#{server[:archive][:ext]}"
         | 
| 105 | 
            -
                  end
         | 
| 106 | 
            -
             | 
| 107 | 
            -
                  archive_cmd = [
         | 
| 108 | 
            -
                    server[:archive][:command],
         | 
| 109 | 
            -
                    server[:archive][:flags],
         | 
| 110 | 
            -
                    ['-f', save_to],
         | 
| 111 | 
            -
                    ['-C', File.dirname(directory)],
         | 
| 112 | 
            -
                    File.basename(directory),
         | 
| 113 | 
            -
                  ]
         | 
| 114 | 
            -
             | 
| 115 | 
            -
                  system(*archive_cmd.flatten)
         | 
| 116 | 
            -
                end
         | 
| 117 | 
            -
             | 
| 127 | 
            +
                # Extract an archive, optionally to a specified directory
         | 
| 128 | 
            +
                # on a remote machine.
         | 
| 129 | 
            +
                # 
         | 
| 118 130 | 
             
                def ssh_extract(archive_path, save_to = File.dirname(archive_path))
         | 
| 119 131 | 
             
                  cmds = [
         | 
| 120 132 | 
             
                    "mkdir -p #{save_to}",
         | 
| @@ -132,6 +144,15 @@ module Marionetta | |
| 132 144 | 
             
                  ssh(cmds.join(' && '))
         | 
| 133 145 | 
             
                end
         | 
| 134 146 |  | 
| 147 | 
            +
                # Using the rsync command copy one file system location to
         | 
| 148 | 
            +
                # another. These may be both local or remote, or a mixture
         | 
| 149 | 
            +
                # of the two.
         | 
| 150 | 
            +
                # 
         | 
| 151 | 
            +
                # Example:
         | 
| 152 | 
            +
                # 
         | 
| 153 | 
            +
                #     rsync('/var/www/logs', '/var/backups/www/logs')
         | 
| 154 | 
            +
                #     rsync('/var/www/logs', 'ubuntu@example.com:/var/backups/www/logs')
         | 
| 155 | 
            +
                # 
         | 
| 135 156 | 
             
                def rsync(from, to)
         | 
| 136 157 | 
             
                  rsync_cmd = [server[:rsync][:command]]
         | 
| 137 158 |  | 
| @@ -144,10 +165,18 @@ module Marionetta | |
| 144 165 | 
             
                  system(*rsync_cmd.flatten)
         | 
| 145 166 | 
             
                end
         | 
| 146 167 |  | 
| 168 | 
            +
                # Short hand for grabbing a file from `:hostname` saving
         | 
| 169 | 
            +
                # to the same location on the local machine unless a path
         | 
| 170 | 
            +
                # is specified. 
         | 
| 171 | 
            +
                # 
         | 
| 147 172 | 
             
                def get(file_path, save_to = File.dirname(file_path))
         | 
| 148 173 | 
             
                  rsync("#{server[:hostname]}:#{file_path}", save_to)
         | 
| 149 174 | 
             
                end
         | 
| 150 175 |  | 
| 176 | 
            +
                # Short hand for putting a file to `:hostname` from the
         | 
| 177 | 
            +
                # local machine to the same location on the remote
         | 
| 178 | 
            +
                # machine unless a path is specified.
         | 
| 179 | 
            +
                # 
         | 
| 151 180 | 
             
                def put(file_path, save_to = File.dirname(file_path))
         | 
| 152 181 | 
             
                  rsync(file_path, "#{server[:hostname]}:#{save_to}")
         | 
| 153 182 | 
             
                end
         | 
    
        data/lib/marionetta/group.rb
    CHANGED
    
    | @@ -1,21 +1,40 @@ | |
| 1 | 
            +
            # `Group` represents a collection of `server` hashes and
         | 
| 2 | 
            +
            # provides `.each_server()` and `.manipulate_each_server` as
         | 
| 3 | 
            +
            # isolated interation methods.
         | 
| 4 | 
            +
            # 
         | 
| 5 | 
            +
            # You can also nest groups within other groups so that
         | 
| 6 | 
            +
            # multiple groups can be operated on at once.
         | 
| 7 | 
            +
            # 
         | 
| 8 | 
            +
            # The external requirement for this file is `celluloid` so we
         | 
| 9 | 
            +
            # can iterate over `@servers` in parallel.
         | 
| 10 | 
            +
            # 
         | 
| 1 11 | 
             
            require 'marionetta'
         | 
| 2 12 | 
             
            require 'marionetta/manipulators'
         | 
| 3 13 | 
             
            require 'celluloid'
         | 
| 4 14 |  | 
| 5 15 | 
             
            module Marionetta
         | 
| 6 16 | 
             
              class Group
         | 
| 7 | 
            -
                attr_reader :name
         | 
| 8 17 |  | 
| 18 | 
            +
                # Group name is currently optional.
         | 
| 19 | 
            +
                # 
         | 
| 9 20 | 
             
                def initialize(name = nil)
         | 
| 10 21 | 
             
                  @name = name
         | 
| 11 22 | 
             
                  @groups = []
         | 
| 12 23 | 
             
                  @servers = []
         | 
| 13 24 | 
             
                end
         | 
| 14 25 |  | 
| 26 | 
            +
                # The name of the group.
         | 
| 27 | 
            +
                # 
         | 
| 28 | 
            +
                attr_reader :name
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                # Nest a group using `.add_group()`.
         | 
| 31 | 
            +
                # 
         | 
| 15 32 | 
             
                def add_group(group)
         | 
| 16 33 | 
             
                  @groups << group
         | 
| 17 34 | 
             
                end
         | 
| 18 35 |  | 
| 36 | 
            +
                # Get all descending groups contained within this group.
         | 
| 37 | 
            +
                # 
         | 
| 19 38 | 
             
                def groups()
         | 
| 20 39 | 
             
                  groups = @groups
         | 
| 21 40 |  | 
| @@ -26,12 +45,25 @@ module Marionetta | |
| 26 45 | 
             
                  return groups
         | 
| 27 46 | 
             
                end
         | 
| 28 47 |  | 
| 48 | 
            +
                # Add a `server` hash or build on the default server in a
         | 
| 49 | 
            +
                # block.
         | 
| 50 | 
            +
                # 
         | 
| 51 | 
            +
                # Example:
         | 
| 52 | 
            +
                # 
         | 
| 53 | 
            +
                #     staging = Marionetta::Group.new(:staging)
         | 
| 54 | 
            +
                #     staging.add_server(:hostname => 'ubuntu@example.com')
         | 
| 55 | 
            +
                #     staging.add_server do |s|
         | 
| 56 | 
            +
                #       s[:hostname] = 'ubuntu@example.com'
         | 
| 57 | 
            +
                #     end
         | 
| 58 | 
            +
                # 
         | 
| 29 59 | 
             
                def add_server(server = nil)
         | 
| 30 60 | 
             
                  server ||= Marionetta.default_server
         | 
| 31 61 | 
             
                  yield server if block_given?
         | 
| 32 62 | 
             
                  @servers << server
         | 
| 33 63 | 
             
                end
         | 
| 34 64 |  | 
| 65 | 
            +
                # Get servers in this group and all descendant groups.
         | 
| 66 | 
            +
                # 
         | 
| 35 67 | 
             
                def servers()
         | 
| 36 68 | 
             
                  servers = @servers
         | 
| 37 69 |  | 
| @@ -42,6 +74,16 @@ module Marionetta | |
| 42 74 | 
             
                  return servers
         | 
| 43 75 | 
             
                end
         | 
| 44 76 |  | 
| 77 | 
            +
                # Iterate over each `server` definition (including nested
         | 
| 78 | 
            +
                # servers) in parallel by passing a block.
         | 
| 79 | 
            +
                # 
         | 
| 80 | 
            +
                #     each_server do |s|
         | 
| 81 | 
            +
                #       cmd = Marionetta::CommandRunner.new(s)
         | 
| 82 | 
            +
                #       cmd.ssh('whoami') do |out|
         | 
| 83 | 
            +
                #         puts out.read
         | 
| 84 | 
            +
                #       end
         | 
| 85 | 
            +
                #     end
         | 
| 86 | 
            +
                # 
         | 
| 45 87 | 
             
                def each_server()
         | 
| 46 88 | 
             
                  futures = []
         | 
| 47 89 |  | 
| @@ -62,6 +104,15 @@ module Marionetta | |
| 62 104 | 
             
                  return return_values
         | 
| 63 105 | 
             
                end
         | 
| 64 106 |  | 
| 107 | 
            +
                # Manipulate each server by passing a manipulator key as
         | 
| 108 | 
            +
                # registered with `Manipulators` and a method name.
         | 
| 109 | 
            +
                # 
         | 
| 110 | 
            +
                # If manipulator cannot be run on a server definition then
         | 
| 111 | 
            +
                # a warn message will be logged.
         | 
| 112 | 
            +
                # 
         | 
| 113 | 
            +
                # If block passed in then the server and return value for
         | 
| 114 | 
            +
                # each server will be passed in when complete.
         | 
| 115 | 
            +
                # 
         | 
| 65 116 | 
             
                def manipulate_each_server(manipulator_name, method_name)
         | 
| 66 117 | 
             
                  each_server do |s|
         | 
| 67 118 | 
             
                    manipulator = Manipulators[manipulator_name].new(s)
         | 
| @@ -144,7 +144,7 @@ module Marionetta | |
| 144 144 | 
             
                    puppet_cmd = ['sudo puppet apply']
         | 
| 145 145 |  | 
| 146 146 | 
             
                    if server[:puppet].has_key?(:modules)
         | 
| 147 | 
            -
                      puppet_cmd <<  | 
| 147 | 
            +
                      puppet_cmd << "--modulepath=#{puppet_tmp}/modules"
         | 
| 148 148 | 
             
                    end
         | 
| 149 149 |  | 
| 150 150 | 
             
                    puppet_cmd << 'manifest.pp'
         | 
| @@ -1,9 +1,25 @@ | |
| 1 | 
            +
            # `Manipulators` is a container for registering manipulators.
         | 
| 2 | 
            +
            # 
         | 
| 3 | 
            +
            # The interface of a manipulator is:
         | 
| 4 | 
            +
            # 
         | 
| 5 | 
            +
            #     self.tasks()        an array of methods to expose via
         | 
| 6 | 
            +
            #                         RakeHelpers *optional*
         | 
| 7 | 
            +
            # 
         | 
| 8 | 
            +
            #     initialize(server)  *required*
         | 
| 9 | 
            +
            #     can?()              *required*
         | 
| 10 | 
            +
            #     
         | 
| 1 11 | 
             
            module Marionetta
         | 
| 2 12 | 
             
              module Manipulators
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                # We automatically require the manipulators packaged with
         | 
| 15 | 
            +
                # this library. *Is this a good idea?*
         | 
| 16 | 
            +
                # 
         | 
| 3 17 | 
             
                require_relative 'manipulators/deployer'
         | 
| 4 18 | 
             
                require_relative 'manipulators/debloyer'
         | 
| 5 19 | 
             
                require_relative 'manipulators/puppet_manipulator'
         | 
| 6 20 |  | 
| 21 | 
            +
                # A hash of all the manipulators.
         | 
| 22 | 
            +
                # 
         | 
| 7 23 | 
             
                def self.all()
         | 
| 8 24 | 
             
                  {
         | 
| 9 25 | 
             
                    :deployer => Deployer,
         | 
| @@ -12,6 +28,8 @@ module Marionetta | |
| 12 28 | 
             
                  }
         | 
| 13 29 | 
             
                end
         | 
| 14 30 |  | 
| 31 | 
            +
                # Get a manipulator.
         | 
| 32 | 
            +
                # 
         | 
| 15 33 | 
             
                def self.[](key)
         | 
| 16 34 | 
             
                  all[key]
         | 
| 17 35 | 
             
                end
         | 
| @@ -1,3 +1,9 @@ | |
| 1 | 
            +
            # `RakeHelper` is provided for those of you who wish to use
         | 
| 2 | 
            +
            # Marionetta in your `Rakefile`.
         | 
| 3 | 
            +
            # 
         | 
| 4 | 
            +
            # One method is provided to expose tasks of a specified group,
         | 
| 5 | 
            +
            # `.install_group_tasks(group)`.
         | 
| 6 | 
            +
            # 
         | 
| 1 7 | 
             
            require 'marionetta'
         | 
| 2 8 | 
             
            require 'marionetta/manipulators'
         | 
| 3 9 | 
             
            require 'rake'
         | 
    
        data/lib/marionetta.rb
    CHANGED
    
    | @@ -1,5 +1,5 @@ | |
| 1 | 
            -
            # Marionetta is a ruby library for executing | 
| 2 | 
            -
            # or more remote machines via SSH.
         | 
| 1 | 
            +
            # [Marionetta][homepage] is a ruby library for executing
         | 
| 2 | 
            +
            # commands on one or more remote machines via SSH.
         | 
| 3 3 | 
             
            # 
         | 
| 4 4 | 
             
            # It provides puppet provisioning without the need for a
         | 
| 5 5 | 
             
            # puppet master and can also deploy your application code
         | 
| @@ -21,12 +21,13 @@ | |
| 21 21 | 
             
            # where you can report issues and send your well thought out
         | 
| 22 22 | 
             
            # pull requests.
         | 
| 23 23 | 
             
            # 
         | 
| 24 | 
            +
            # [homepage]: http://drpheltright.github.com/marionetta/
         | 
| 25 | 
            +
            # [github]: https://github.com/DrPheltRight/marionetta/
         | 
| 24 26 | 
             
            # [author]: http://lukemorton.co.uk
         | 
| 25 | 
            -
            # [github]: https://github.com/DrPheltRight/marionetta
         | 
| 26 27 | 
             
            # 
         | 
| 27 28 | 
             
            module Marionetta
         | 
| 28 29 |  | 
| 29 | 
            -
              VERSION = '0.4. | 
| 30 | 
            +
              VERSION = '0.4.2'
         | 
| 30 31 |  | 
| 31 32 | 
             
              ### Defining Servers
         | 
| 32 33 |  | 
    
        data/marionetta.gemspec
    CHANGED
    
    | @@ -3,10 +3,14 @@ require File.dirname(__FILE__)+'/lib/marionetta' | |
| 3 3 | 
             
            Gem::Specification.new do |s|
         | 
| 4 4 | 
             
              s.name        = "marionetta"
         | 
| 5 5 | 
             
              s.version     = Marionetta::VERSION
         | 
| 6 | 
            +
              
         | 
| 6 7 | 
             
              s.homepage    = 'https://github.com/DrPheltRight/marionetta'
         | 
| 8 | 
            +
             | 
| 7 9 | 
             
              s.authors     = ["Luke Morton"]
         | 
| 8 10 | 
             
              s.email       = ["lukemorton.dev@gmail.com"]
         | 
| 11 | 
            +
             | 
| 9 12 | 
             
              s.summary     = "Provision using puppet and deploy your servers over SSH."
         | 
| 13 | 
            +
             | 
| 10 14 | 
             
              s.description = "Marionetta is a ruby library for executing commands on one
         | 
| 11 15 | 
             
                               or more remote machines via SSH. It provides puppet
         | 
| 12 16 | 
             
                               provisioning without the need for a puppet master and can
         | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: marionetta
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.4. | 
| 4 | 
            +
              version: 0.4.2
         | 
| 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: 2012-09- | 
| 12 | 
            +
            date: 2012-09-28 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: open4
         |