small_wonder 0.1
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 +80 -0
- data/bin/small_wonder +5 -0
- data/lib/small_wonder.rb +87 -0
- data/lib/small_wonder/application.rb +109 -0
- data/lib/small_wonder/cli.rb +64 -0
- data/lib/small_wonder/config.rb +9 -0
- data/lib/small_wonder/configuratorator.rb +88 -0
- data/lib/small_wonder/deploy.rb +100 -0
- data/lib/small_wonder/log.rb +30 -0
- data/lib/small_wonder/salticid_monkeypatch.rb +22 -0
- data/lib/small_wonder/utils.rb +62 -0
- metadata +213 -0
    
        data/README.md
    ADDED
    
    | @@ -0,0 +1,80 @@ | |
| 1 | 
            +
            ## Small Wonder
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            Small Wonder is a deployment tool.
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            Specifically it's a lightweight wrapper for Salticid, which is a tool that will run a bunch of commands on a bunch of machines for you. Kinda like capistrano. To a large extent Small Wonder is a means for using Salticid and Chef together to deploy applications. Salticid "roles" and "tasks" are Ruby code and can use the Chef Ruby APIs to search and access anything Chef Client or Knife can (by consuming your knife config). Small Wonder has a notion of an application object as well, this object contains data like the name, version, status and even the databag config data for an application. Name, version and status are saved back to the Chef node so you can use application specific details in your recipes, searches and etc.
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            ### License
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            Apache License Version 2.0
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            ### Usage
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            #### Configure
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                $ mkdir ~/.small_wonder
         | 
| 16 | 
            +
                $ $EDITOR ~/.small_wonder/small_wonder.rb
         | 
| 17 | 
            +
             | 
| 18 | 
            +
             | 
| 19 | 
            +
                chef_repo_path                      "/path/to/chef-repo" # the path to your chef-repo
         | 
| 20 | 
            +
                ssh_user                            "user" # the ssh user you want Small Wonder and Salticid to use
         | 
| 21 | 
            +
                application_deployment_attribute    "deployed_applications" # the attribute Small Wonder will save data back to chef using
         | 
| 22 | 
            +
                config_template_working_directory   "/tmp/small_wonder" # the local directory temporary template work will be done in
         | 
| 23 | 
            +
                remote_working_dir                  "/tmp/small_wonder_#{Time.now.to_i}" # the remote directory temporary template work will be done in
         | 
| 24 | 
            +
                application_deployments_dir         "/path/to/chef-repo/application_deployments" # path to your role files
         | 
| 25 | 
            +
                application_templates_dir           "/path/to/chef-repo/application_templates" # path to your template files
         | 
| 26 | 
            +
                databag                             "apps" # the databag that contains application configuration data items
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            In addition to a Small Wonder config, you need a working Knife configuration file.
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            #### Expectations
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            Small Wonder expects the names of things to match up. Specifically the application name should be the same as the Salticid role name, databag item and template directory. Note that if an application name has a hyphen in it Small Wonder will convert it to an underscore internally with regards to Salticid roles since they need to be legit Ruby method names.
         | 
| 33 | 
            +
             | 
| 34 | 
            +
            #### Roles and tasks
         | 
| 35 | 
            +
             | 
| 36 | 
            +
            Define "roles" using ruby code and splitting deployment operations in to "tasks".
         | 
| 37 | 
            +
             | 
| 38 | 
            +
            Check out the examples directory for ... well examples.
         | 
| 39 | 
            +
             | 
| 40 | 
            +
            #### Running small_wonder
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            A role name should match the name of the application, Chef recipe and databag item your app uses for configuration.
         | 
| 43 | 
            +
             | 
| 44 | 
            +
            To run the role use:
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                $ small_wonder -p example
         | 
| 47 | 
            +
             | 
| 48 | 
            +
            If you have a specific Chef search query you want to use to as the source of nodes to deploy to:
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                $ small_wonder -p example -q "some:attribute"
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            The default search query is "recipes:example".
         | 
| 53 | 
            +
             | 
| 54 | 
            +
            You can also specify a version number to pass to the application object:
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                $ small_wonder -p example -V 123
         | 
| 57 | 
            +
             | 
| 58 | 
            +
            #### Chef Data
         | 
| 59 | 
            +
             | 
| 60 | 
            +
            During the deployment Small Wonder will save data back to the Chef node. Moreover, status is updated to the task currently being run, so you can see live deployment progress in your searches.
         | 
| 61 | 
            +
             | 
| 62 | 
            +
            If you want to find out details about an app use a knife search like:
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                $ knife search node "deployed_applications:example" -a deployed_applications
         | 
| 65 | 
            +
             | 
| 66 | 
            +
            The output should be something like:
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                deployed_applications:
         | 
| 69 | 
            +
                  example:
         | 
| 70 | 
            +
                    status:   final
         | 
| 71 | 
            +
                    version:  851
         | 
| 72 | 
            +
                id:                              your.server.com
         | 
| 73 | 
            +
             | 
| 74 | 
            +
            #### Templating
         | 
| 75 | 
            +
             | 
| 76 | 
            +
            Small wonder supports some basic erb templating just like Chef. All erb files in your 'application_templates_dir' can get populated with data from various Chef objects and attributes. Those files then get scp'd to the server and "overlayed" (read: `cp -R`) on to the installation directory of the application.
         | 
| 77 | 
            +
             | 
| 78 | 
            +
            Files and directories with 'VERSION' in the name will be convered to have the actual appclication.version variable in place of 'VERSION'.
         | 
| 79 | 
            +
             | 
| 80 | 
            +
            The examples directory has some examples.
         | 
    
        data/bin/small_wonder
    ADDED
    
    
    
        data/lib/small_wonder.rb
    ADDED
    
    | @@ -0,0 +1,87 @@ | |
| 1 | 
            +
            require 'rubygems'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'socket'
         | 
| 4 | 
            +
            require 'excon'
         | 
| 5 | 
            +
            require 'mixlib/config'
         | 
| 6 | 
            +
            require 'mixlib/cli'
         | 
| 7 | 
            +
            require 'mixlib/log'
         | 
| 8 | 
            +
            require 'yajl/json_gem'
         | 
| 9 | 
            +
            require 'salticid'
         | 
| 10 | 
            +
            require 'chef'
         | 
| 11 | 
            +
            require 'highline/import'
         | 
| 12 | 
            +
            require 'erb'
         | 
| 13 | 
            +
            require 'net/scp'
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            __DIR__ = File.dirname(__FILE__)
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            $LOAD_PATH.unshift __DIR__ unless
         | 
| 18 | 
            +
              $LOAD_PATH.include?(__DIR__) ||
         | 
| 19 | 
            +
              $LOAD_PATH.include?(File.expand_path(__DIR__))
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            require 'small_wonder/log'
         | 
| 22 | 
            +
            require 'small_wonder/cli'
         | 
| 23 | 
            +
            require 'small_wonder/config'
         | 
| 24 | 
            +
            require 'small_wonder/utils'
         | 
| 25 | 
            +
            require 'small_wonder/deploy'
         | 
| 26 | 
            +
            require 'small_wonder/application'
         | 
| 27 | 
            +
            require 'small_wonder/configuratorator'
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            require 'small_wonder/salticid_monkeypatch'
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            module SmallWonder
         | 
| 32 | 
            +
              class << self
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def salticid=(h)
         | 
| 35 | 
            +
                  @salticid = h
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                def salticid
         | 
| 39 | 
            +
                  @salticid
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                def main()
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  cli = SmallWonder::CLI.new
         | 
| 45 | 
            +
                  cli.parse_options
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  # consume small wonder config
         | 
| 48 | 
            +
                  SmallWonder::Utils.consume_config_file(cli, cli.config[:config_file])
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  unless SmallWonder::Utils.sane_working_dir?(SmallWonder::Config.remote_working_dir)
         | 
| 51 | 
            +
                    SmallWonder::Log.error("Your remote working dir looks strange (#{SmallWonder::Config.remote_working_dir})")
         | 
| 52 | 
            +
                    exit(1)
         | 
| 53 | 
            +
                  end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                  unless SmallWonder::Utils.sane_working_dir?(SmallWonder::Config.config_template_working_directory)
         | 
| 56 | 
            +
                    SmallWonder::Log.error("Your local working dir looks strange (#{SmallWonder::Config.config_template_working_directory})")
         | 
| 57 | 
            +
                    exit(1)
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                  SmallWonder::Utils.consume_config_file(cli, cli.config[:knife_config_file])
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                  # inintialize chef/knife config
         | 
| 63 | 
            +
                  Chef::Config[:node_name] = SmallWonder::Config.node_name
         | 
| 64 | 
            +
                  Chef::Config[:client_key] = SmallWonder::Config.client_key
         | 
| 65 | 
            +
                  Chef::Config[:chef_server_url] = SmallWonder::Config.chef_server_url
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                  # Set up salticid
         | 
| 68 | 
            +
                  self.salticid = Salticid.new
         | 
| 69 | 
            +
                  self.salticid.load(File.expand_path(File.join(
         | 
| 70 | 
            +
                    SmallWonder::Config.application_deployments_dir, '**', '*.rb')))
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                  case SmallWonder::Config.action
         | 
| 73 | 
            +
                  when "none"
         | 
| 74 | 
            +
                    SmallWonder::Log.info("No action specified, assuming deploy ...")
         | 
| 75 | 
            +
                    SmallWonder::Deploy.run
         | 
| 76 | 
            +
                  when "deploy"
         | 
| 77 | 
            +
                    SmallWonder::Deploy.run
         | 
| 78 | 
            +
                  when "vicki"
         | 
| 79 | 
            +
                    system("open http://www.youtube.com/watch?v=ukSvjqwJixw")
         | 
| 80 | 
            +
                  else
         | 
| 81 | 
            +
                    SmallWonder::Log.error("Supported action \"#{SmallWonder::Config.action}\"!")
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
              end
         | 
| 87 | 
            +
            end
         | 
| @@ -0,0 +1,109 @@ | |
| 1 | 
            +
            module SmallWonder
         | 
| 2 | 
            +
              class Application
         | 
| 3 | 
            +
             | 
| 4 | 
            +
                ## In small wonder an application object has the following constituent parts:
         | 
| 5 | 
            +
                #### node name
         | 
| 6 | 
            +
                #### application name
         | 
| 7 | 
            +
                #### version
         | 
| 8 | 
            +
                #### status
         | 
| 9 | 
            +
                #### databag-based config data
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                attr_accessor :version, :status
         | 
| 12 | 
            +
                attr_reader :node_name, :application_name, :config_data
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                def initialize(node_name, application_name, opts = {})
         | 
| 15 | 
            +
                  @node_name = node_name
         | 
| 16 | 
            +
                  @application_name = application_name
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  @config_data = Chef::DataBagItem.load(SmallWonder::Config.databag, application_name).raw_data
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  data = Chef::Node.load(node_name)
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  unless data[SmallWonder::Config.application_deployment_attribute]
         | 
| 23 | 
            +
                    create_application_deployment_attribute(node_name, application_name)
         | 
| 24 | 
            +
                    data = Chef::Node.load(node_name)
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  unless data[SmallWonder::Config.application_deployment_attribute].has_key?(application_name)
         | 
| 28 | 
            +
                    create_application_deployment_attribute_child(node_name, application_name)
         | 
| 29 | 
            +
                    data = Chef::Node.load(node_name)
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  unless data[SmallWonder::Config.application_deployment_attribute][application_name]
         | 
| 33 | 
            +
                    update_application_data(node_name, application_name, "version", "0")
         | 
| 34 | 
            +
                    update_application_data(node_name, application_name, "status", "new")
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  # set version to application version if we have one
         | 
| 38 | 
            +
                  @version = opts[:version] || nil
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  # set status to status before we update for deploy
         | 
| 41 | 
            +
                  @status = opts[:status] || get_status(node_name, application_name)
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                  # save the data back to the chef node
         | 
| 44 | 
            +
                  update_application_data(node_name, application_name, "status", "initialized")
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                def version=(version)
         | 
| 48 | 
            +
                  @version = version
         | 
| 49 | 
            +
                  set_version(@node_name, @application_name, version)
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                def status=(status)
         | 
| 53 | 
            +
                  @status = status
         | 
| 54 | 
            +
                  set_status(@node_name, @application_name, status)
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                private
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                def get_existing_version(node, application)
         | 
| 60 | 
            +
                  version = get_chef_data_value(node, application, "version")
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                  unless version
         | 
| 63 | 
            +
                    version = "0"
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  version
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                def set_version(node, application, version)
         | 
| 70 | 
            +
                  update_application_data(node, application, "version", version)
         | 
| 71 | 
            +
                end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                def get_status(node, application)
         | 
| 74 | 
            +
                  get_chef_data_value(node, application, "status")
         | 
| 75 | 
            +
                end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                def set_status(node, application, status)
         | 
| 78 | 
            +
                  update_application_data(node, application, "status", status)
         | 
| 79 | 
            +
                end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                def get_chef_data_value(node, application, key)
         | 
| 82 | 
            +
                  data = Chef::Node.load(node)
         | 
| 83 | 
            +
                  data[SmallWonder::Config.application_deployment_attribute][application][key]
         | 
| 84 | 
            +
                end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                def create_application_deployment_attribute(node, application)
         | 
| 87 | 
            +
                  data = Chef::Node.load(node)
         | 
| 88 | 
            +
                  data[SmallWonder::Config.application_deployment_attribute] = {}
         | 
| 89 | 
            +
                  data[SmallWonder::Config.application_deployment_attribute][application] = {}
         | 
| 90 | 
            +
                  data.save
         | 
| 91 | 
            +
                end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                def create_application_deployment_attribute_child(node, application)
         | 
| 94 | 
            +
                  data = Chef::Node.load(node)
         | 
| 95 | 
            +
                  data[SmallWonder::Config.application_deployment_attribute][application] = {}
         | 
| 96 | 
            +
                  data.save
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                def update_application_data(node, application, key, value)
         | 
| 100 | 
            +
                  data = Chef::Node.load(node)
         | 
| 101 | 
            +
                  data[SmallWonder::Config.application_deployment_attribute][application][key] = value
         | 
| 102 | 
            +
                  data.save
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                  #SmallWonder::Log.info("[#{node} // #{application}] #{key} was updated to #{value}")
         | 
| 105 | 
            +
                end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
              end
         | 
| 108 | 
            +
            end
         | 
| 109 | 
            +
             | 
| @@ -0,0 +1,64 @@ | |
| 1 | 
            +
            module SmallWonder
         | 
| 2 | 
            +
              class CLI
         | 
| 3 | 
            +
                include Mixlib::CLI
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                option :config_file,
         | 
| 6 | 
            +
                  :short => "-c CONFIG",
         | 
| 7 | 
            +
                  :long  => "--config CONFIG",
         | 
| 8 | 
            +
                  :default => "#{ENV['HOME']}/.small_wonder/small_wonder.rb",
         | 
| 9 | 
            +
                  :description => "The configuration file to use"
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                option :knife_config_file,
         | 
| 12 | 
            +
                  :short => "-k CONFIG",
         | 
| 13 | 
            +
                  :long  => "--knife CONFIG",
         | 
| 14 | 
            +
                  :default => "#{ENV['HOME']}/.chef/knife.rb",
         | 
| 15 | 
            +
                  :description => "The knife configuration file to use"
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                option :action,
         | 
| 18 | 
            +
                  :short => "-a ACTION",
         | 
| 19 | 
            +
                  :long  => "--action ACTION",
         | 
| 20 | 
            +
                  :description => "The action you want small wonder to take [deploy]",
         | 
| 21 | 
            +
                  :default => "none"
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                option :app,
         | 
| 24 | 
            +
                  :short => "-p APP",
         | 
| 25 | 
            +
                  :long  => "--app APP",
         | 
| 26 | 
            +
                  :description => "The app you want small wonder to do something with",
         | 
| 27 | 
            +
                  :default => nil
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                option :version,
         | 
| 30 | 
            +
                  :short => "-V VERSION",
         | 
| 31 | 
            +
                  :long  => "--version VERSION",
         | 
| 32 | 
            +
                  :description => "The version of app you want small wonder to do something with",
         | 
| 33 | 
            +
                  :default => nil
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                option :query,
         | 
| 36 | 
            +
                  :short => "-q query",
         | 
| 37 | 
            +
                  :long  => "--query query",
         | 
| 38 | 
            +
                  :description => "The query you want small wonder to do something with",
         | 
| 39 | 
            +
                  :default => nil
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                option :write_node_file,
         | 
| 42 | 
            +
                  :short => "-w",
         | 
| 43 | 
            +
                  :long  => "--write",
         | 
| 44 | 
            +
                  :description => "true/false write the node json file to your chef-repo at end of deploy",
         | 
| 45 | 
            +
                  :default => false
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                option :log_level,
         | 
| 48 | 
            +
                  :short => "-l LEVEL",
         | 
| 49 | 
            +
                  :long  => "--log_level LEVEL",
         | 
| 50 | 
            +
                  :description => "Set the log level (debug, info, warn, error, fatal)",
         | 
| 51 | 
            +
                  :default => :info,
         | 
| 52 | 
            +
                  :proc => Proc.new { |l| l.to_sym }
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                option :help,
         | 
| 55 | 
            +
                  :short => "-h",
         | 
| 56 | 
            +
                  :long => "--help",
         | 
| 57 | 
            +
                  :description => "All the help you need ...",
         | 
| 58 | 
            +
                  :on => :tail,
         | 
| 59 | 
            +
                  :boolean => true,
         | 
| 60 | 
            +
                  :show_options => true,
         | 
| 61 | 
            +
                  :exit => 0
         | 
| 62 | 
            +
             | 
| 63 | 
            +
              end
         | 
| 64 | 
            +
            end
         | 
| @@ -0,0 +1,88 @@ | |
| 1 | 
            +
            module SmallWonder
         | 
| 2 | 
            +
              class Configuratorator
         | 
| 3 | 
            +
             | 
| 4 | 
            +
                def self.generate_and_upload_files(application, path, opts = {})
         | 
| 5 | 
            +
                  config_template_dir = "#{SmallWonder::Config.application_templates_dir}/#{application.application_name}"
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                  templates = Dir["#{config_template_dir}/**/*.erb"]
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  file_list = []
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  templates.each do |file|
         | 
| 12 | 
            +
                    filename = File.basename(file, '.*')
         | 
| 13 | 
            +
                    filename.gsub!("VERSION", application.version)
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                    reldir = File.dirname(file).gsub("#{config_template_dir}", ".")
         | 
| 16 | 
            +
                    reldir.gsub!("VERSION", application.version)
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    file_list << "#{reldir}/#{filename}"
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                    SmallWonder::Log.info("Generating #{reldir}/#{filename}")
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                    generated_file = generate_file(file, application, config_template_dir, opts)
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    file_dir = "#{SmallWonder::Config.config_template_working_directory}/#{application.application_name}/#{reldir}"
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                    FileUtils.mkdir_p(file_dir)
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    SmallWonder::Utils.write_file(generated_file, "#{file_dir}/#{filename}")
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  upload_files(application.node_name, application.application_name)
         | 
| 32 | 
            +
                  copy_files_to_install_dir(application.node_name, application.application_name, path)
         | 
| 33 | 
            +
                  cleanup_working_directories(application.node_name, application.application_name)
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  file_list
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                private
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                def self.generate_file(file, application, config_template_dir, opts)
         | 
| 41 | 
            +
                  @deploy_config = application.config_data
         | 
| 42 | 
            +
                  @options = @deploy_config
         | 
| 43 | 
            +
                  @node = Chef::Node.load(application.node_name)
         | 
| 44 | 
            +
                  node = Chef::Node.load(application.node_name)
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  if opts[:app_options]
         | 
| 47 | 
            +
                    @app_options = opts[:app_options]
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  template_file = File.read(file)
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  begin
         | 
| 53 | 
            +
                    template = ERB.new(template_file)
         | 
| 54 | 
            +
                  rescue Exception => e
         | 
| 55 | 
            +
                    SmallWonder::Log.error("Error generating file (#{file}).")
         | 
| 56 | 
            +
                    SmallWonder::Log.error(e)
         | 
| 57 | 
            +
                    exit(1)
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                  template.result(binding)
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                def self.upload_files(node_name, application)
         | 
| 64 | 
            +
                  Net::SSH.start(node_name, SmallWonder::Config.ssh_user) do |ssh|
         | 
| 65 | 
            +
                    ssh.exec!("mkdir -p #{SmallWonder::Config.remote_working_dir}")
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  Net::SCP.start(node_name, SmallWonder::Config.ssh_user) do |scp|
         | 
| 69 | 
            +
                    scp.upload!("#{SmallWonder::Config.config_template_working_directory}/#{application}", SmallWonder::Config.remote_working_dir, {:recursive => true})
         | 
| 70 | 
            +
                  end
         | 
| 71 | 
            +
                end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                def self.copy_files_to_install_dir(node_name, application, path)
         | 
| 74 | 
            +
                  Net::SSH.start(node_name, SmallWonder::Config.ssh_user) do |ssh|
         | 
| 75 | 
            +
                    ssh.exec!("echo \"#{@sudo_password}\n\" | sudo -S cp -R #{SmallWonder::Config.remote_working_dir}/#{application}/* /#{path}/")
         | 
| 76 | 
            +
                  end
         | 
| 77 | 
            +
                end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                def self.cleanup_working_directories(node_name, application)
         | 
| 80 | 
            +
                  FileUtils.rm_rf("#{SmallWonder::Config.config_template_working_directory}/#{application}")
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                  Net::SSH.start(node_name, SmallWonder::Config.ssh_user) do |ssh|
         | 
| 83 | 
            +
                    ssh.exec!("rm -rf #{SmallWonder::Config.remote_working_dir}")
         | 
| 84 | 
            +
                  end
         | 
| 85 | 
            +
                end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
              end
         | 
| 88 | 
            +
            end
         | 
| @@ -0,0 +1,100 @@ | |
| 1 | 
            +
            module SmallWonder
         | 
| 2 | 
            +
              class Deploy
         | 
| 3 | 
            +
                def self.run()
         | 
| 4 | 
            +
                  if SmallWonder::Config.app
         | 
| 5 | 
            +
                    nodes = SmallWonder::Deploy.node_query()
         | 
| 6 | 
            +
                    SmallWonder::Deploy.deploy(SmallWonder::Config.app, nodes)
         | 
| 7 | 
            +
                  else
         | 
| 8 | 
            +
                    SmallWonder::Log.error("No application was specified for your deploy, use the '-p' switch.")
         | 
| 9 | 
            +
                  end
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                private
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                def self.node_query()
         | 
| 15 | 
            +
                  nodes = []
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  if SmallWonder::Config.query
         | 
| 18 | 
            +
                    query = SmallWonder::Config.query
         | 
| 19 | 
            +
                  else
         | 
| 20 | 
            +
                    query = "recipes:#{SmallWonder::Config.app}"
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  Chef::Search::Query.new.search(:node, "#{query}") do |n|
         | 
| 24 | 
            +
                    nodes << n[:fqdn]
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  nodes
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def self.deploy(application_name, nodes)
         | 
| 31 | 
            +
                  if nodes.length > 0
         | 
| 32 | 
            +
                    SmallWonder::Log.info("Found the following nodes via your search.")
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    nodes.each do |node|
         | 
| 35 | 
            +
                      SmallWonder::Log.info("*  #{node}")
         | 
| 36 | 
            +
                    end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                    input = ::HighLine.agree("Are you sure you want to deploy to these nodes [yes|no]?")
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    if input
         | 
| 41 | 
            +
                      SmallWonder::Log.info("Commencing deployment.")
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                      nodes.each do |node|
         | 
| 44 | 
            +
                        if SmallWonder::Config.version
         | 
| 45 | 
            +
                          SmallWonder::Log.info("Got version #{SmallWonder::Config.version} from a command line option, using it as the current version for #{SmallWonder::Config.app}.")
         | 
| 46 | 
            +
                          application = SmallWonder::Application.new(node, application_name, {:version => SmallWonder::Config.version})
         | 
| 47 | 
            +
                        else
         | 
| 48 | 
            +
                          SmallWonder::Log.info("Did not get a app version to deploy on the command line, assuming you will set it during the deploy.")
         | 
| 49 | 
            +
                          application = SmallWonder::Application.new(node, application_name)
         | 
| 50 | 
            +
                        end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                        deploy_application(application)
         | 
| 53 | 
            +
                      end
         | 
| 54 | 
            +
                    end
         | 
| 55 | 
            +
                  else
         | 
| 56 | 
            +
                    SmallWonder::Log.info("No nodes found for your search.")
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                def self.deploy_application(application)
         | 
| 61 | 
            +
                  run_salticid_task(application)
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                  if SmallWonder::Config.write_node_file
         | 
| 64 | 
            +
                    SmallWonder::Utils.write_node_data_file(application.node_name)
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                ## deploy step
         | 
| 69 | 
            +
                # Creates a new salticid host for node, and calls <app>.deploy on it.
         | 
| 70 | 
            +
                def self.run_salticid_task(application)
         | 
| 71 | 
            +
                  host = SmallWonder.salticid.host application.node_name
         | 
| 72 | 
            +
                  host.on_log do |message|
         | 
| 73 | 
            +
                    begin
         | 
| 74 | 
            +
                      level = {'stderr' => 'fatal', nil => 'info'}
         | 
| 75 | 
            +
                      level.default = 'info'
         | 
| 76 | 
            +
                      severity = message.severity || 'info'
         | 
| 77 | 
            +
                      SmallWonder::Log.send(level[message.severity], message.text)
         | 
| 78 | 
            +
                    rescue => e
         | 
| 79 | 
            +
                      p e
         | 
| 80 | 
            +
                    end
         | 
| 81 | 
            +
                  end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                  host.application = application
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                  host.user SmallWonder::Config.ssh_user
         | 
| 86 | 
            +
                  @sudo_password = ::HighLine.ask("Your sudo password please:  ") { |q| q.echo = false }
         | 
| 87 | 
            +
                  host.password = @sudo_password
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                  # sub hyphens for underscores to work around having hyphens in method names
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                  host.role application.application_name.gsub("-", "_")
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                  host.send(application.application_name.gsub("-", "_")).deploy
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                  # set the application status to final since the deploy is done
         | 
| 96 | 
            +
                  application.status = "final"
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
              end
         | 
| 100 | 
            +
            end
         | 
| @@ -0,0 +1,30 @@ | |
| 1 | 
            +
            module SmallWonder
         | 
| 2 | 
            +
              require 'colorize'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
              class Log
         | 
| 5 | 
            +
                COLORS = {
         | 
| 6 | 
            +
                  'FATAL' => :light_red,
         | 
| 7 | 
            +
                  'ERROR' => :light_red,
         | 
| 8 | 
            +
                  'WARN' => :light_yellow,
         | 
| 9 | 
            +
                  'INFO' => nil,
         | 
| 10 | 
            +
                  'DEBUG' => :light_blue
         | 
| 11 | 
            +
                }
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                class Formatter < Mixlib::Log::Formatter
         | 
| 14 | 
            +
                  def call(severity, time, progname, msg)
         | 
| 15 | 
            +
                    str = format("[%s] %s\n",
         | 
| 16 | 
            +
                                 time.strftime("%H:%M:%S"),
         | 
| 17 | 
            +
                                 msg).colorize(COLORS[severity])
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                extend Mixlib::Log
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                def self.init(*opts)
         | 
| 24 | 
            +
                  super *opts
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  @logger.formatter = Formatter.new() if @logger.respond_to?(:formatter=)
         | 
| 27 | 
            +
                  @logger
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
            end
         | 
| @@ -0,0 +1,22 @@ | |
| 1 | 
            +
            class ::Salticid::Host
         | 
| 2 | 
            +
              def application
         | 
| 3 | 
            +
                @application
         | 
| 4 | 
            +
              end
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              def application=(app)
         | 
| 7 | 
            +
                @application = app
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
            end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            class Salticid::Task
         | 
| 12 | 
            +
              alias_method :stock_run, :run
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              def run(context = nil, *args)
         | 
| 15 | 
            +
                if context.respond_to? :application
         | 
| 16 | 
            +
                  context.application.status = name
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                SmallWonder::Log.info("Running task: #{name}")
         | 
| 20 | 
            +
                stock_run(context, *args)
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
            end
         | 
| @@ -0,0 +1,62 @@ | |
| 1 | 
            +
            module SmallWonder
         | 
| 2 | 
            +
              class Utils
         | 
| 3 | 
            +
             | 
| 4 | 
            +
                def self.consume_config_file(cli, file)
         | 
| 5 | 
            +
                  if File.exists?(file)
         | 
| 6 | 
            +
                    SmallWonder::Config.from_file(file)
         | 
| 7 | 
            +
                    SmallWonder::Config.merge!(cli.config)
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                    SmallWonder::Log.level(SmallWonder::Config.log_level)
         | 
| 10 | 
            +
                  else
         | 
| 11 | 
            +
                    SmallWonder::Log.error("#{file} doesn't exist!")
         | 
| 12 | 
            +
                    exit(1)
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def self.write_node_data_file(node)
         | 
| 17 | 
            +
                  data = Chef::Node.load(node)
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  file = "#{SmallWonder::Config.chef_repo_path}/nodes/#{node}.json"
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  SmallWonder::Log.info("Writing node file (#{file}).")
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  write_file(JSON.pretty_generate(data), file)
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def self.write_file(data, path)
         | 
| 27 | 
            +
                  begin
         | 
| 28 | 
            +
                    file = File.new(path,"w")
         | 
| 29 | 
            +
                    file.puts data
         | 
| 30 | 
            +
                  rescue Exception => e
         | 
| 31 | 
            +
                    SmallWonder::Log.error("Error writing file (#{path}).")
         | 
| 32 | 
            +
                    SmallWonder::Log.error(e)
         | 
| 33 | 
            +
                    exit(1)
         | 
| 34 | 
            +
                  ensure
         | 
| 35 | 
            +
                    file.close
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                def self.sane_working_dir?(dir)
         | 
| 40 | 
            +
                  sane = []
         | 
| 41 | 
            +
                  sane << starts_with?(dir, "/tmp/small_wonder")
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                  if dir.include?("..")
         | 
| 44 | 
            +
                    sane << false
         | 
| 45 | 
            +
                  else
         | 
| 46 | 
            +
                    sane << true
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  if sane.include?(false)
         | 
| 50 | 
            +
                    false
         | 
| 51 | 
            +
                  else
         | 
| 52 | 
            +
                    true
         | 
| 53 | 
            +
                  end
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                def self.starts_with?(string, prefix)
         | 
| 57 | 
            +
                  prefix = prefix.to_s
         | 
| 58 | 
            +
                  string[0, prefix.length] == prefix
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
              end
         | 
| 62 | 
            +
            end
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,213 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification 
         | 
| 2 | 
            +
            name: small_wonder
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version 
         | 
| 4 | 
            +
              hash: 9
         | 
| 5 | 
            +
              prerelease: 
         | 
| 6 | 
            +
              segments: 
         | 
| 7 | 
            +
              - 0
         | 
| 8 | 
            +
              - 1
         | 
| 9 | 
            +
              version: "0.1"
         | 
| 10 | 
            +
            platform: ruby
         | 
| 11 | 
            +
            authors: 
         | 
| 12 | 
            +
            - Joe Williams
         | 
| 13 | 
            +
            autorequire: 
         | 
| 14 | 
            +
            bindir: bin
         | 
| 15 | 
            +
            cert_chain: []
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            date: 2012-07-27 00:00:00 Z
         | 
| 18 | 
            +
            dependencies: 
         | 
| 19 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 20 | 
            +
              name: mixlib-config
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              requirement: &id001 !ruby/object:Gem::Requirement 
         | 
| 23 | 
            +
                none: false
         | 
| 24 | 
            +
                requirements: 
         | 
| 25 | 
            +
                - - ">="
         | 
| 26 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 27 | 
            +
                    hash: 3
         | 
| 28 | 
            +
                    segments: 
         | 
| 29 | 
            +
                    - 0
         | 
| 30 | 
            +
                    version: "0"
         | 
| 31 | 
            +
              type: :runtime
         | 
| 32 | 
            +
              version_requirements: *id001
         | 
| 33 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 34 | 
            +
              name: mixlib-cli
         | 
| 35 | 
            +
              prerelease: false
         | 
| 36 | 
            +
              requirement: &id002 !ruby/object:Gem::Requirement 
         | 
| 37 | 
            +
                none: false
         | 
| 38 | 
            +
                requirements: 
         | 
| 39 | 
            +
                - - ">="
         | 
| 40 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 41 | 
            +
                    hash: 3
         | 
| 42 | 
            +
                    segments: 
         | 
| 43 | 
            +
                    - 0
         | 
| 44 | 
            +
                    version: "0"
         | 
| 45 | 
            +
              type: :runtime
         | 
| 46 | 
            +
              version_requirements: *id002
         | 
| 47 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 48 | 
            +
              name: mixlib-log
         | 
| 49 | 
            +
              prerelease: false
         | 
| 50 | 
            +
              requirement: &id003 !ruby/object:Gem::Requirement 
         | 
| 51 | 
            +
                none: false
         | 
| 52 | 
            +
                requirements: 
         | 
| 53 | 
            +
                - - ">="
         | 
| 54 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 55 | 
            +
                    hash: 3
         | 
| 56 | 
            +
                    segments: 
         | 
| 57 | 
            +
                    - 0
         | 
| 58 | 
            +
                    version: "0"
         | 
| 59 | 
            +
              type: :runtime
         | 
| 60 | 
            +
              version_requirements: *id003
         | 
| 61 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 62 | 
            +
              name: excon
         | 
| 63 | 
            +
              prerelease: false
         | 
| 64 | 
            +
              requirement: &id004 !ruby/object:Gem::Requirement 
         | 
| 65 | 
            +
                none: false
         | 
| 66 | 
            +
                requirements: 
         | 
| 67 | 
            +
                - - ">="
         | 
| 68 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 69 | 
            +
                    hash: 3
         | 
| 70 | 
            +
                    segments: 
         | 
| 71 | 
            +
                    - 0
         | 
| 72 | 
            +
                    version: "0"
         | 
| 73 | 
            +
              type: :runtime
         | 
| 74 | 
            +
              version_requirements: *id004
         | 
| 75 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 76 | 
            +
              name: yajl-ruby
         | 
| 77 | 
            +
              prerelease: false
         | 
| 78 | 
            +
              requirement: &id005 !ruby/object:Gem::Requirement 
         | 
| 79 | 
            +
                none: false
         | 
| 80 | 
            +
                requirements: 
         | 
| 81 | 
            +
                - - ">="
         | 
| 82 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 83 | 
            +
                    hash: 3
         | 
| 84 | 
            +
                    segments: 
         | 
| 85 | 
            +
                    - 0
         | 
| 86 | 
            +
                    version: "0"
         | 
| 87 | 
            +
              type: :runtime
         | 
| 88 | 
            +
              version_requirements: *id005
         | 
| 89 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 90 | 
            +
              name: chef
         | 
| 91 | 
            +
              prerelease: false
         | 
| 92 | 
            +
              requirement: &id006 !ruby/object:Gem::Requirement 
         | 
| 93 | 
            +
                none: false
         | 
| 94 | 
            +
                requirements: 
         | 
| 95 | 
            +
                - - ">="
         | 
| 96 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 97 | 
            +
                    hash: 3
         | 
| 98 | 
            +
                    segments: 
         | 
| 99 | 
            +
                    - 0
         | 
| 100 | 
            +
                    version: "0"
         | 
| 101 | 
            +
              type: :runtime
         | 
| 102 | 
            +
              version_requirements: *id006
         | 
| 103 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 104 | 
            +
              name: highline
         | 
| 105 | 
            +
              prerelease: false
         | 
| 106 | 
            +
              requirement: &id007 !ruby/object:Gem::Requirement 
         | 
| 107 | 
            +
                none: false
         | 
| 108 | 
            +
                requirements: 
         | 
| 109 | 
            +
                - - ">="
         | 
| 110 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 111 | 
            +
                    hash: 3
         | 
| 112 | 
            +
                    segments: 
         | 
| 113 | 
            +
                    - 0
         | 
| 114 | 
            +
                    version: "0"
         | 
| 115 | 
            +
              type: :runtime
         | 
| 116 | 
            +
              version_requirements: *id007
         | 
| 117 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 118 | 
            +
              name: net-ssh
         | 
| 119 | 
            +
              prerelease: false
         | 
| 120 | 
            +
              requirement: &id008 !ruby/object:Gem::Requirement 
         | 
| 121 | 
            +
                none: false
         | 
| 122 | 
            +
                requirements: 
         | 
| 123 | 
            +
                - - ">="
         | 
| 124 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 125 | 
            +
                    hash: 3
         | 
| 126 | 
            +
                    segments: 
         | 
| 127 | 
            +
                    - 0
         | 
| 128 | 
            +
                    version: "0"
         | 
| 129 | 
            +
              type: :runtime
         | 
| 130 | 
            +
              version_requirements: *id008
         | 
| 131 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 132 | 
            +
              name: net-scp
         | 
| 133 | 
            +
              prerelease: false
         | 
| 134 | 
            +
              requirement: &id009 !ruby/object:Gem::Requirement 
         | 
| 135 | 
            +
                none: false
         | 
| 136 | 
            +
                requirements: 
         | 
| 137 | 
            +
                - - ">="
         | 
| 138 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 139 | 
            +
                    hash: 3
         | 
| 140 | 
            +
                    segments: 
         | 
| 141 | 
            +
                    - 0
         | 
| 142 | 
            +
                    version: "0"
         | 
| 143 | 
            +
              type: :runtime
         | 
| 144 | 
            +
              version_requirements: *id009
         | 
| 145 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 146 | 
            +
              name: salticid
         | 
| 147 | 
            +
              prerelease: false
         | 
| 148 | 
            +
              requirement: &id010 !ruby/object:Gem::Requirement 
         | 
| 149 | 
            +
                none: false
         | 
| 150 | 
            +
                requirements: 
         | 
| 151 | 
            +
                - - ">="
         | 
| 152 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 153 | 
            +
                    hash: 3
         | 
| 154 | 
            +
                    segments: 
         | 
| 155 | 
            +
                    - 0
         | 
| 156 | 
            +
                    version: "0"
         | 
| 157 | 
            +
              type: :runtime
         | 
| 158 | 
            +
              version_requirements: *id010
         | 
| 159 | 
            +
            description: 
         | 
| 160 | 
            +
            email: j@boundary.com
         | 
| 161 | 
            +
            executables: 
         | 
| 162 | 
            +
            - small_wonder
         | 
| 163 | 
            +
            extensions: []
         | 
| 164 | 
            +
             | 
| 165 | 
            +
            extra_rdoc_files: 
         | 
| 166 | 
            +
            - README.md
         | 
| 167 | 
            +
            files: 
         | 
| 168 | 
            +
            - bin/small_wonder
         | 
| 169 | 
            +
            - lib/small_wonder/application.rb
         | 
| 170 | 
            +
            - lib/small_wonder/cli.rb
         | 
| 171 | 
            +
            - lib/small_wonder/config.rb
         | 
| 172 | 
            +
            - lib/small_wonder/configuratorator.rb
         | 
| 173 | 
            +
            - lib/small_wonder/deploy.rb
         | 
| 174 | 
            +
            - lib/small_wonder/log.rb
         | 
| 175 | 
            +
            - lib/small_wonder/salticid_monkeypatch.rb
         | 
| 176 | 
            +
            - lib/small_wonder/utils.rb
         | 
| 177 | 
            +
            - lib/small_wonder.rb
         | 
| 178 | 
            +
            - README.md
         | 
| 179 | 
            +
            homepage: https://github.com/boundary/small_wonder
         | 
| 180 | 
            +
            licenses: []
         | 
| 181 | 
            +
             | 
| 182 | 
            +
            post_install_message: 
         | 
| 183 | 
            +
            rdoc_options: []
         | 
| 184 | 
            +
             | 
| 185 | 
            +
            require_paths: 
         | 
| 186 | 
            +
            - lib
         | 
| 187 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement 
         | 
| 188 | 
            +
              none: false
         | 
| 189 | 
            +
              requirements: 
         | 
| 190 | 
            +
              - - ">="
         | 
| 191 | 
            +
                - !ruby/object:Gem::Version 
         | 
| 192 | 
            +
                  hash: 3
         | 
| 193 | 
            +
                  segments: 
         | 
| 194 | 
            +
                  - 0
         | 
| 195 | 
            +
                  version: "0"
         | 
| 196 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement 
         | 
| 197 | 
            +
              none: false
         | 
| 198 | 
            +
              requirements: 
         | 
| 199 | 
            +
              - - ">="
         | 
| 200 | 
            +
                - !ruby/object:Gem::Version 
         | 
| 201 | 
            +
                  hash: 3
         | 
| 202 | 
            +
                  segments: 
         | 
| 203 | 
            +
                  - 0
         | 
| 204 | 
            +
                  version: "0"
         | 
| 205 | 
            +
            requirements: []
         | 
| 206 | 
            +
             | 
| 207 | 
            +
            rubyforge_project: 
         | 
| 208 | 
            +
            rubygems_version: 1.8.24
         | 
| 209 | 
            +
            signing_key: 
         | 
| 210 | 
            +
            specification_version: 3
         | 
| 211 | 
            +
            summary: A Deployment Tool
         | 
| 212 | 
            +
            test_files: []
         | 
| 213 | 
            +
             |