├── .gitignore ├── .rspec ├── .travis.yml ├── Gemfile ├── Gemfile.lock ├── LICENSE.txt ├── README.md ├── Rakefile ├── Vagrantfile ├── lib ├── tasks │ ├── cane.rake │ ├── reek.rake │ └── rspec.rake ├── vagrant-shell-commander.rb └── vagrant-shell-commander │ ├── action.rb │ ├── command.rb │ ├── config.rb │ ├── option_manager.rb │ ├── plugin.rb │ └── version.rb ├── spec ├── action_spec.rb ├── command_spec.rb ├── config_spec.rb ├── option_manager_spec.rb ├── plugin_spec.rb └── spec_helper.rb └── vagrant-shell-commander.gemspec /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | InstalledFiles 7 | _yardoc 8 | coverage 9 | doc/ 10 | lib/bundler/man 11 | pkg 12 | rdoc 13 | spec/reports 14 | test/tmp 15 | test/version_tmp 16 | tmp 17 | .ruby-* 18 | *~ 19 | .vagrant 20 | *gem 21 | debug* -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format doc 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.1.2 4 | install: bundle install 5 | script: bundle exec rake 6 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in vagrant-shell-commander.gemspec 4 | gemspec 5 | 6 | group :development do 7 | gem "vagrant", :git => "git://github.com/mitchellh/vagrant.git", tag: 'v1.6.2' 8 | end 9 | 10 | group :plugins do 11 | gem "vagrant-shell-commander", path: "." 12 | end 13 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GIT 2 | remote: git://github.com/mitchellh/vagrant.git 3 | revision: 970ed5068ede4f6aae4b51e2cabe872aa924cacf 4 | tag: v1.6.2 5 | specs: 6 | vagrant (1.6.2) 7 | bundler (>= 1.5.2, < 1.7.0) 8 | childprocess (~> 0.5.0) 9 | erubis (~> 2.7.0) 10 | i18n (~> 0.6.0) 11 | listen (~> 2.7.1) 12 | log4r (~> 1.1.9, < 1.1.11) 13 | net-scp (~> 1.1.0) 14 | net-ssh (>= 2.6.6, < 2.10.0) 15 | rb-kqueue (~> 0.2.0) 16 | wdm (~> 0.1.0) 17 | winrm (~> 1.1.3) 18 | 19 | PATH 20 | remote: . 21 | specs: 22 | vagrant-shell-commander (0.3.2) 23 | 24 | GEM 25 | remote: https://rubygems.org/ 26 | specs: 27 | akami (1.2.2) 28 | gyoku (>= 0.4.0) 29 | nokogiri 30 | builder (3.2.2) 31 | cane (2.6.0) 32 | parallel 33 | celluloid (0.15.2) 34 | timers (~> 1.1.0) 35 | childprocess (0.5.3) 36 | ffi (~> 1.0, >= 1.0.11) 37 | columnize (0.8.9) 38 | debugger-linecache (1.2.0) 39 | debugger2 (1.0.0.beta2) 40 | columnize (>= 0.3.1) 41 | debugger-linecache (~> 1.2.0) 42 | diff-lcs (1.2.4) 43 | erubis (2.7.0) 44 | ffi (1.9.3) 45 | gssapi (1.0.3) 46 | ffi (>= 1.0.1) 47 | gyoku (1.1.1) 48 | builder (>= 2.1.2) 49 | httpclient (2.3.4.1) 50 | httpi (0.9.7) 51 | rack 52 | i18n (0.6.9) 53 | listen (2.7.5) 54 | celluloid (>= 0.15.2) 55 | rb-fsevent (>= 0.9.3) 56 | rb-inotify (>= 0.9) 57 | little-plugger (1.1.3) 58 | log4r (1.1.10) 59 | logging (1.8.1) 60 | little-plugger (>= 1.1.3) 61 | multi_json (>= 1.3.6) 62 | mini_portile (0.6.0) 63 | multi_json (1.7.7) 64 | net-scp (1.1.2) 65 | net-ssh (>= 2.6.5) 66 | net-ssh (2.9.1) 67 | nokogiri (1.6.2.1) 68 | mini_portile (= 0.6.0) 69 | nori (1.1.5) 70 | parallel (0.7.1) 71 | rack (1.5.2) 72 | rake (10.1.0) 73 | rb-fsevent (0.9.4) 74 | rb-inotify (0.9.4) 75 | ffi (>= 0.5.0) 76 | rb-kqueue (0.2.2) 77 | ffi (>= 0.5.0) 78 | reek (1.3.1) 79 | ruby2ruby (~> 2.0.2) 80 | ruby_parser (~> 3.1.1) 81 | sexp_processor 82 | rspec (2.14.1) 83 | rspec-core (~> 2.14.0) 84 | rspec-expectations (~> 2.14.0) 85 | rspec-mocks (~> 2.14.0) 86 | rspec-core (2.14.5) 87 | rspec-expectations (2.14.2) 88 | diff-lcs (>= 1.1.3, < 2.0) 89 | rspec-mocks (2.14.3) 90 | ruby2ruby (2.0.6) 91 | ruby_parser (~> 3.1) 92 | sexp_processor (~> 4.0) 93 | ruby_parser (3.1.3) 94 | sexp_processor (~> 4.1) 95 | rubyntlm (0.1.1) 96 | savon (0.9.5) 97 | akami (~> 1.0) 98 | builder (>= 2.1.2) 99 | gyoku (>= 0.4.0) 100 | httpi (~> 0.9) 101 | nokogiri (>= 1.4.0) 102 | nori (~> 1.0) 103 | wasabi (~> 1.0) 104 | sexp_processor (4.2.1) 105 | simplecov (0.7.1) 106 | multi_json (~> 1.0) 107 | simplecov-html (~> 0.7.1) 108 | simplecov-html (0.7.1) 109 | timers (1.1.0) 110 | uuidtools (2.1.4) 111 | wasabi (1.0.0) 112 | nokogiri (>= 1.4.0) 113 | wdm (0.1.0) 114 | winrm (1.1.3) 115 | gssapi (~> 1.0.0) 116 | httpclient (~> 2.2, >= 2.2.0.2) 117 | logging (~> 1.6, >= 1.6.1) 118 | nokogiri (~> 1.5) 119 | rubyntlm (~> 0.1.1) 120 | savon (= 0.9.5) 121 | uuidtools (~> 2.1.2) 122 | yard (0.8.6.2) 123 | 124 | PLATFORMS 125 | ruby 126 | 127 | DEPENDENCIES 128 | bundler (~> 1.3) 129 | cane 130 | debugger2 131 | rake 132 | reek 133 | rspec 134 | simplecov 135 | vagrant! 136 | vagrant-shell-commander! 137 | yard 138 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Federico Gimenez Nieto 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/fgimenez/vagrant-shell-commander.png)](https://travis-ci.org/fgimenez/vagrant-shell-commander) 2 | [![Code Climate](https://codeclimate.com/github/fgimenez/vagrant-shell-commander.png)](https://codeclimate.com/github/fgimenez/vagrant-shell-commander) 3 | 4 | # Vagrant::Shell::Commander 5 | 6 | Vagrant plugin for running arbitrary shell commands on guest(s). Executes the given command on all the machines of multinode environments, or just in one of them. It also gives the option to specify a working directory and a user by command line. 7 | 8 | It allows you to hook into the boot process, specifying a command to be run once the machine has finished the complete boot process. This can be handy for running init scripts which depends on files located in shared folders, for example. 9 | 10 | 11 | ## Installation 12 | 13 | Given you have Vagrant 1.1+ installed, as usual with vagrant plugins: 14 | 15 | $ vagrant plugin install vagrant-shell-commander 16 | 17 | 18 | ## Usage 19 | 20 | The command line utility is very similar to the ```vagrant ssh -c``` builtin command, but with multinode support and additional options. To execute a command on all the machines: 21 | 22 | $ vagrant sh -c free 23 | 24 | Restrict the machine to run: 25 | 26 | $ vagrant sh -c free machine1 27 | 28 | Specify the working directory (remember to quote multiword commands): 29 | 30 | $ vagrant sh -c 'ls -al' -d /srv/www 31 | 32 | Execute as specific user: 33 | 34 | $ vagrant sh -c 'cap production deploy' -u 'deployer' 35 | 36 | Get help: 37 | 38 | $ vagrant sh -h 39 | 40 | To execute a command once the machine(s) has booted, add this configuration option to the Vagrantfile: 41 | 42 | ```ruby 43 | Vagrant.configure("2") do |config| 44 | # other... 45 | 46 | config.sh.after_share_folders = "sudo /etc/init.d/myapp start" 47 | 48 | end 49 | ``` 50 | 51 | This can be handy if, for example, you want an application to start on boot and the application's code is in a synced folder. 52 | 53 | ## Contributing 54 | 55 | 1. Fork it 56 | 2. Create your feature branch (`git checkout -b my-new-feature`) 57 | 3. Add and implement your specs 58 | 4. Commit your changes (`git commit -am 'Add some feature'`) 59 | 5. Push to the branch (`git push origin my-new-feature`) 60 | 6. Create new Pull Request 61 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | 3 | Dir.glob('lib/tasks/*.rake').each { |r| import r } 4 | 5 | task default: [:reek, :spec, :quality] 6 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | Vagrant.configure("2") do |config| 2 | config.vm.box = "canonical-ubuntu-12.04" 3 | config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/precise/current/precise-server-cloudimg-amd64-vagrant-disk1.box" 4 | 5 | config.vm.define "node1" 6 | config.vm.define "node2" 7 | 8 | config.sh.after_share_folders = "free" 9 | end 10 | -------------------------------------------------------------------------------- /lib/tasks/cane.rake: -------------------------------------------------------------------------------- 1 | begin 2 | require 'cane/rake_task' 3 | 4 | desc "Run cane to check quality metrics" 5 | Cane::RakeTask.new(:quality) do |cane| 6 | cane.abc_max = 15 7 | cane.add_threshold 'coverage/.last_run.json', :>=, 95 8 | cane.no_style = true 9 | end 10 | rescue LoadError 11 | warn "cane not available, quality task not provided." 12 | end 13 | -------------------------------------------------------------------------------- /lib/tasks/reek.rake: -------------------------------------------------------------------------------- 1 | require 'reek/rake/task' 2 | 3 | Reek::Rake::Task.new do |t| 4 | t.fail_on_error = false 5 | end 6 | -------------------------------------------------------------------------------- /lib/tasks/rspec.rake: -------------------------------------------------------------------------------- 1 | begin 2 | require 'rspec/core/rake_task' 3 | RSpec::Core::RakeTask.new(:spec) 4 | rescue LoadError 5 | warn "rspec not available, spec task not provided." 6 | end 7 | -------------------------------------------------------------------------------- /lib/vagrant-shell-commander.rb: -------------------------------------------------------------------------------- 1 | require "vagrant-shell-commander/version" 2 | require "vagrant-shell-commander/option_manager" 3 | require "vagrant-shell-commander/plugin" 4 | require 'vagrant-shell-commander/command' 5 | require 'vagrant-shell-commander/config' 6 | require 'vagrant-shell-commander/action' 7 | 8 | -------------------------------------------------------------------------------- /lib/vagrant-shell-commander/action.rb: -------------------------------------------------------------------------------- 1 | require "log4r" 2 | 3 | module VagrantPlugins 4 | module ShellCommander 5 | # Action for shell command hooking 6 | class Action 7 | include Vagrant::Action::Builtin 8 | 9 | # Constructor 10 | # 11 | # @param app [Action] Next middleware to call 12 | # @param env [Hash] Action environment 13 | # @return nil 14 | # 15 | def initialize(app, env) 16 | @app = app 17 | @env = env 18 | @logger = Log4r::Logger.new("vagrant::plugins::shell-commander::action") 19 | end 20 | 21 | # Call method of this middleware 22 | # 23 | # @param env [Hash] Action environment 24 | # @return nil 25 | # 26 | def call(env) 27 | @logger.debug("hook fired for action #{@env[:action_name]}, machine_action: #{@env[:machine_action]}") 28 | @app.call(env) 29 | if @env[:machine] && @env[:machine].state.id != :poweroff && 30 | ! @env[:machine].config.sh.after_share_folders.nil? 31 | @env[:machine].action(:ssh_run, 32 | ssh_run_command: @env[:machine].config.sh.after_share_folders, 33 | ssh_opts: {extra_args: []}) 34 | end 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/vagrant-shell-commander/command.rb: -------------------------------------------------------------------------------- 1 | module VagrantPlugins 2 | module ShellCommander 3 | # Main plugin command 4 | class Command < Vagrant.plugin("2", "command") 5 | attr_accessor :env 6 | 7 | # Main entry point of this command 8 | # 9 | def execute 10 | cli_options = OptionManager.new.execute 11 | argv = parse_options(cli_options[:parser]) 12 | 13 | return unless argv 14 | 15 | if [nil, ''].include? cli_options[:values][:cmd] 16 | env.ui.info cli_options[:parser] 17 | else 18 | with_target_vms(argv) do |machine| 19 | manage_machine(machine, cli_options) 20 | end 21 | end 22 | 0 23 | end 24 | 25 | private 26 | 27 | # Executes actions for a given machine 28 | # 29 | # @param [Vagrant::Machine] subject vm 30 | # @param [Hash] Parser (:parser key) and parsed options (:values key) 31 | # @return nil 32 | # 33 | def manage_machine(machine, cli_options) 34 | if machine.state.id != :running 35 | env.ui.warn("Machine #{machine.name} is not running.") 36 | return 37 | end 38 | 39 | env.ui.success("#{machine.name}::") 40 | machine.action(:ssh_run, 41 | ssh_run_command: add_options_to_command(cli_options[:values][:cmd], 42 | cli_options[:values][:dir], 43 | cli_options[:values][:user]), 44 | ssh_opts: {extra_args: []}) 45 | end 46 | 47 | # Adds the options to the given command 48 | # 49 | # @param cmd [String] Shell command 50 | # @param cwd [String] Optional working directory 51 | # @param user [String] Optional executing user 52 | # @return [String] Command with directory change if cwd is present and optional executing user 53 | # 54 | def add_options_to_command(cmd, cwd=nil, user=nil) 55 | cmd = "cd #{cwd} && #{cmd}" if cwd 56 | cmd = "sudo su - #{user} -c \"#{cmd}\"" if user 57 | cmd 58 | end 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /lib/vagrant-shell-commander/config.rb: -------------------------------------------------------------------------------- 1 | module VagrantPlugins 2 | module ShellCommander 3 | # Configuration options 4 | class Config < Vagrant.plugin("2", "config") 5 | attr_accessor :after_share_folders 6 | 7 | # Initialize override, setting config options default values 8 | # for merging 9 | def initialize 10 | super 11 | 12 | @after_share_folders = UNSET_VALUE 13 | end 14 | 15 | # finalize! override, unseting config options 16 | def finalize! 17 | @after_share_folders = nil if @after_share_folders == UNSET_VALUE 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/vagrant-shell-commander/option_manager.rb: -------------------------------------------------------------------------------- 1 | module VagrantPlugins 2 | module ShellCommander 3 | # Option parser 4 | # 5 | class OptionManager 6 | # Main parsing method 7 | # @return [Hash] The keys are :parser for the object returned by 8 | # OptionParser and :values for the actual option values 9 | # 10 | def execute 11 | options = {} 12 | block = lambda do |parser| 13 | parser.banner = "Usage: vagrant sh -c 'COMMAND' -d [DIR] [MACHINE]" 14 | 15 | parser.separator '' 16 | 17 | parser.on('-d [DIR]', '--directory [DIR]', 18 | 'Directory to execute the command') do |dir| 19 | options[:dir] = dir 20 | end 21 | 22 | parser.on('-u [USER]', '--user [USER]', 23 | 'User to execute the command') do |user| 24 | options[:user] = user 25 | end 26 | 27 | parser.on("-c 'COMMAND'", "--command 'COMMAND'", 28 | 'Command to execute, quotes required for multiword') do |cmd| 29 | options[:cmd] = cmd 30 | end 31 | end 32 | 33 | {parser: OptionParser.new(&block), values: options} 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/vagrant-shell-commander/plugin.rb: -------------------------------------------------------------------------------- 1 | module VagrantPlugins 2 | module ShellCommander 3 | # Plugin definition 4 | # 5 | class Plugin < Vagrant.plugin("2") 6 | name 'vagrant shell commander' 7 | 8 | command 'sh' do 9 | Command 10 | end 11 | 12 | config 'sh' do 13 | Config 14 | end 15 | 16 | action_hook :sh_hook do |hook| 17 | hook.after(Vagrant::Action::Builtin::Provision, Action) 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/vagrant-shell-commander/version.rb: -------------------------------------------------------------------------------- 1 | module VagrantPlugins 2 | module ShellCommander 3 | VERSION = "0.3.2" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/action_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe VagrantPlugins::ShellCommander::Action do 4 | let(:app) {double(call: true)} 5 | let(:state) {double(id: :poweron)} 6 | let(:cmd) {'cmd'} 7 | let(:sh) {double(after_share_folders: cmd)} 8 | let(:config) {double(sh: sh)} 9 | let(:machine_env) {double(config: config, state: state, action: true)} 10 | let(:action_env) {{machine: machine_env}} 11 | let(:env) {double(:[] => machine_env)} 12 | let(:subject) {described_class.new(app, env)} 13 | 14 | describe "#call" do 15 | it "should call the next middleware" do 16 | expect(app).to receive(:call).with(action_env) 17 | 18 | subject.call(action_env) 19 | end 20 | 21 | describe "SSHRun call" do 22 | it "should call SSHRun action of the current machine with the after_boot option as command" do 23 | expect(machine_env).to receive(:action).with(:ssh_run, ssh_run_command: cmd, ssh_opts: {extra_args: []}) 24 | 25 | subject.call(action_env) 26 | end 27 | 28 | it "should not call SSHRun action if after_share_folder option is nil" do 29 | machine_env = double(config: config, state: state) 30 | action_env = {machine: machine_env} 31 | 32 | expect(machine_env).not_to receive(:action) 33 | 34 | subject.call(action_env) 35 | end 36 | 37 | it "should not call SSHRun action if machine is not powered on" do 38 | state = double(id: :poweroff) 39 | machine_env = double(config: config, state: state, action: true) 40 | action_env = {machine: machine_env} 41 | 42 | expect(machine_env).not_to receive(:action) 43 | 44 | subject.call(action_env) 45 | end 46 | 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /spec/command_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe VagrantPlugins::ShellCommander::Command do 4 | let(:subject) {described_class.new('2', 'command')} 5 | let(:argv) {double} 6 | let(:opts) {{parser: 'parser', values: {cmd: 'cmd'}}} 7 | 8 | before(:each) do 9 | VagrantPlugins::ShellCommander::OptionManager.stub_chain(:new, :execute). 10 | and_return(opts) 11 | 12 | subject.stub(:with_target_vms) 13 | subject.stub(:parse_options).and_return(argv) 14 | end 15 | 16 | describe '#execute' do 17 | it 'returns 0 if all went good' do 18 | expect(subject.execute).to eql(0) 19 | end 20 | 21 | describe 'options' do 22 | after(:each) do 23 | subject.execute 24 | end 25 | 26 | it 'parses the given options' do 27 | subject.should_receive(:parse_options).with(opts[:parser]) 28 | end 29 | 30 | it 'returns on option parsing failure' do 31 | subject.stub(:parse_options).and_return(false) 32 | 33 | expect(subject.execute).to eql(nil) 34 | end 35 | 36 | end 37 | 38 | describe 'command execution' do 39 | let(:machine) {double} 40 | let(:ui) {double(info: true, warn: true)} 41 | let(:env) {double(ui: ui)} 42 | let(:machine_name) {'machine_name'} 43 | 44 | before(:each) do 45 | subject.stub(:with_target_vms).and_yield(machine) 46 | subject.stub(:env).and_return env 47 | machine.stub(:name).and_return(machine_name) 48 | end 49 | 50 | after(:each) do 51 | subject.execute 52 | end 53 | 54 | context 'not running machine' do 55 | before(:each) do 56 | machine.stub_chain(:state, :id).and_return(:not_running) 57 | end 58 | 59 | it 'reports information about state' do 60 | expect(ui).to receive(:warn).with("Machine #{machine_name} is not running.") 61 | end 62 | 63 | it 'dows not try to execute the command' do 64 | expect(machine).not_to receive(:communicate) 65 | end 66 | end 67 | 68 | context 'running machine' do 69 | let(:cmd) {'command'} 70 | let(:dir) {'dir'} 71 | let(:user) {'user'} 72 | let(:communicate) {double(execute: true)} 73 | 74 | before(:each) do 75 | VagrantPlugins::ShellCommander::OptionManager.stub_chain(:new, :execute). 76 | and_return(parser: 'parser', values: {cmd: cmd}) 77 | 78 | machine.stub_chain(:state, :id).and_return(:running) 79 | allow(ui).to receive(:success) 80 | allow(machine).to receive(:action) 81 | end 82 | 83 | it 'executes the given command' do 84 | expect(machine).to receive(:action).with(:ssh_run, ssh_run_command: cmd, ssh_opts: {:extra_args=>[]}) 85 | end 86 | 87 | it 'shows the machine name' do 88 | expect(ui).to receive(:success).with("#{machine_name}::") 89 | end 90 | 91 | it 'executes the command in the given dir' do 92 | VagrantPlugins::ShellCommander::OptionManager.stub_chain(:new, :execute). 93 | and_return(parser: 'parser', values: {cmd: cmd, dir: dir}) 94 | 95 | expect(machine).to receive(:action).with(:ssh_run, 96 | ssh_run_command: "cd #{dir} && #{cmd}", 97 | ssh_opts: {:extra_args=>[]}) 98 | end 99 | 100 | it 'executes the command for the given user' do 101 | VagrantPlugins::ShellCommander::OptionManager.stub_chain(:new, :execute). 102 | and_return(parser: 'parser', values: {cmd: cmd, user: user}) 103 | 104 | expect(machine).to receive(:action).with(:ssh_run, 105 | ssh_run_command: "sudo su - #{user} -c \"#{cmd}\"", 106 | ssh_opts: {:extra_args=>[]}) 107 | end 108 | 109 | it 'executes the command for the given user and the given dir' do 110 | VagrantPlugins::ShellCommander::OptionManager.stub_chain(:new, :execute). 111 | and_return(parser: 'parser', values: {cmd: cmd, user: user, dir: dir}) 112 | 113 | expect(machine).to receive(:action).with(:ssh_run, 114 | ssh_run_command: "sudo su - #{user} -c \"cd #{dir} && #{cmd}\"", 115 | ssh_opts: {:extra_args=>[]}) 116 | end 117 | 118 | describe 'shows help' do 119 | let(:parser) {'parser'} 120 | 121 | after(:each) do 122 | expect(subject).not_to receive(:with_target_vms) 123 | expect(ui).to receive(:info).with(parser) 124 | end 125 | 126 | it 'an empty command' do 127 | VagrantPlugins::ShellCommander::OptionManager.stub_chain(:new, :execute). 128 | and_return(parser: parser, values: {cmd: '', dir: dir}) 129 | end 130 | 131 | it 'non present command' do 132 | VagrantPlugins::ShellCommander::OptionManager.stub_chain(:new, :execute). 133 | and_return(parser: parser, values: {user: user}) 134 | end 135 | end 136 | end 137 | end 138 | end 139 | end 140 | -------------------------------------------------------------------------------- /spec/config_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe VagrantPlugins::ShellCommander::Config do 4 | 5 | let(:subject) {described_class.new} 6 | 7 | describe 'config properies' do 8 | it 'should have a after_share_folders attr_accessor' do 9 | expect(subject).to respond_to(:after_share_folders) 10 | expect(subject).to respond_to(:after_share_folders=) 11 | end 12 | end 13 | 14 | describe "#initialize" do 15 | it 'should initialize after_share_folders' do 16 | expect(subject.after_share_folders).to be_equal(Vagrant::Plugin::V2::Config::UNSET_VALUE) 17 | end 18 | end 19 | 20 | describe "#finalize!" do 21 | it 'should deinitialize after_share_folders if it has been set' do 22 | config = described_class.new 23 | config.finalize! 24 | expect(config.after_share_folders).to be_nil 25 | end 26 | 27 | it 'should not deinitialize after_share_folders if it has not been set' do 28 | config = described_class.new 29 | config.after_share_folders = 'value' 30 | config.finalize! 31 | expect(config.after_share_folders).to eq('value') 32 | end 33 | 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /spec/option_manager_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe VagrantPlugins::ShellCommander::OptionManager do 4 | let(:subject) {described_class.new} 5 | let(:argv) {double()} 6 | 7 | describe '#execute' do 8 | let(:option_parser) {double(:banner= => true, 9 | separator: true, 10 | on: true)} 11 | 12 | before(:each) do 13 | OptionParser.stub(:new). 14 | and_yield(option_parser) 15 | 16 | subject.stub(:parse_options).and_return(true) 17 | end 18 | 19 | after(:each) do 20 | 21 | end 22 | 23 | it 'displays a informing banner' do 24 | option_parser.should_receive(:banner=) 25 | 26 | subject.execute 27 | end 28 | 29 | it 'has a dir option' do 30 | dir_value = 'dir' 31 | 32 | option_parser.stub(:on).with('-d [DIR]', 33 | anything, 34 | anything). 35 | and_yield(dir_value) 36 | 37 | result = subject.execute 38 | 39 | expect(result[:values][:dir]).to eql(dir_value) 40 | end 41 | 42 | it 'has a cmd option' do 43 | cmd_value = 'cmd' 44 | 45 | option_parser.stub(:on).with("-c 'COMMAND'", 46 | anything, 47 | anything). 48 | and_yield(cmd_value) 49 | 50 | result = subject.execute 51 | 52 | expect(result[:values][:cmd]).to eql(cmd_value) 53 | end 54 | 55 | it 'has a user option' do 56 | user_value = 'user' 57 | 58 | option_parser.stub(:on).with('-u [USER]', 59 | anything, 60 | anything). 61 | and_yield(user_value) 62 | 63 | result = subject.execute 64 | 65 | expect(result[:values][:user]).to eql(user_value) 66 | end 67 | 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /spec/plugin_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe VagrantPlugins::ShellCommander::Plugin do 4 | it 'should have a name' do 5 | expect(described_class.name).not_to be_nil 6 | end 7 | 8 | it "should define a command of type Command" do 9 | default_command = described_class.components.commands[:sh] 10 | expect(default_command[1]).to eql(primary: true) 11 | end 12 | 13 | it "should define a config of type Config" do 14 | default_config = described_class.components.configs[:top].to_hash[:"sh"] 15 | expect(default_config).to be(VagrantPlugins::ShellCommander::Config) 16 | end 17 | 18 | context 'action hooks' do 19 | let(:hook) {double(append: true, prepend: true)} 20 | 21 | it 'should define an action hook for after share folders' do 22 | hook_proc = described_class.components.action_hooks[:__all_actions__][0] 23 | hook = double 24 | expect(hook).to receive(:after).with(Vagrant::Action::Builtin::Provision, VagrantPlugins::ShellCommander::Action) 25 | hook_proc.call(hook) 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'simplecov' 2 | SimpleCov.start 3 | 4 | require 'vagrant' 5 | 6 | Dir['lib/**/*.rb'].each {|f| require File.expand_path(f)} 7 | 8 | RSpec.configure do |config| 9 | config.treat_symbols_as_metadata_keys_with_true_values = true 10 | config.run_all_when_everything_filtered = true 11 | config.filter_run :focus 12 | 13 | 14 | # Run specs in random order to surface order dependencies. If you find an 15 | # order dependency and want to debug it, you can fix the order by providing 16 | # the seed, which is printed after each run. 17 | # --seed 1234 18 | config.order = 'random' 19 | end 20 | -------------------------------------------------------------------------------- /vagrant-shell-commander.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'vagrant-shell-commander/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "vagrant-shell-commander" 8 | spec.version = VagrantPlugins::ShellCommander::VERSION 9 | spec.authors = ["Federico Gimenez Nieto"] 10 | spec.email = ["federico.gimenez@gmail.com"] 11 | spec.description = %q{Vagrant plugin for executing shell commands on guests machines and hooking them in the boot process} 12 | spec.summary = %q{Some variations to shell command execution on guest(s)} 13 | spec.homepage = "https://github.com/fgimenez/vagrant-shell-commander" 14 | spec.license = "MIT" 15 | 16 | spec.files = `git ls-files`.split($/) 17 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 18 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 19 | spec.require_paths = ["lib"] 20 | 21 | spec.add_development_dependency "bundler", "~> 1.3" 22 | spec.add_development_dependency "rake" 23 | spec.add_development_dependency "rspec" 24 | spec.add_development_dependency "reek" 25 | spec.add_development_dependency "cane" 26 | spec.add_development_dependency "yard" 27 | spec.add_development_dependency "simplecov" 28 | spec.add_development_dependency "debugger2" 29 | end 30 | --------------------------------------------------------------------------------