diff --git a/.rubocop.yml b/.rubocop.yml index 57e0758..5ddab99 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -4,5 +4,7 @@ AllCops: - bin/** Encoding: Enabled: false +ClassLength: + Enabled: false MethodLength: - Max: 30 + Enabled: false diff --git a/Gemfile b/Gemfile index 324f5d6..d237f59 100644 --- a/Gemfile +++ b/Gemfile @@ -6,17 +6,18 @@ group :development do # We depend on Vagrant for development, but we don't add it as a # gem dependency because we expect to be installed within the # Vagrant environment itself using `vagrant plugin`. - gem "vagrant", :git => "git://github.com/mitchellh/vagrant.git", :tag => "v1.3.5" + gem 'vagrant', git: 'git://github.com/mitchellh/vagrant.git', tag: 'v1.3.5' + gem 'vagrant-windows', '~> 1.2.0' end group :acceptance do - gem "vagrant-digitalocean", "~> 0.4.0" - gem "vagrant-aws", "~> 0.4.0" - gem "vagrant-rackspace", "~> 0.1.4" + gem 'vagrant-digitalocean', '~> 0.4.0' + gem 'vagrant-aws', '~> 0.4.0' + gem 'vagrant-rackspace', '~> 0.1.4' end group :docs do - gem "yard", "~> 0.8.5" - gem "redcarpet", "~> 2.2.2" - gem "github-markup", "~> 0.7.5" + gem 'yard', '~> 0.8.5' + gem 'redcarpet', '~> 2.2.2' + gem 'github-markup', '~> 0.7.5' end diff --git a/Rakefile b/Rakefile index de5b652..ff02bd7 100644 --- a/Rakefile +++ b/Rakefile @@ -4,16 +4,27 @@ require 'rspec/core/rake_task' require 'rubocop/rake_task' require 'yard' -Rubocop::RakeTask.new +# rubocop:disable LineLength + YARD::Rake::YardocTask.new +Rubocop::RakeTask.new(:rubocop) do |task| + task.patterns = [ + '**/*.rb', + '**/Vagrantfile', + '*.gemspec', + 'Gemfile', + 'Rakefile' + ] +end + namespace :test do RSpec::Core::RakeTask.new(:unit) do |t| - t.pattern = "test/unit/**/*_spec.rb" + t.pattern = 'test/unit/**/*_spec.rb' end - desc "Run acceptance tests..these actually launch Vagrant sessions." + desc 'Run acceptance tests..these actually launch Vagrant sessions.' task :acceptance, :provider do |t, args| # ensure all required dummy boxen are installed @@ -24,10 +35,12 @@ namespace :test do end unless system("vagrant box list | grep 'digital_ocean' &>/dev/null") - system("vagrant box add digital_ocean https://github.com/smdahlen/vagrant-digitalocean/raw/master/box/digital_ocean.box") + system('vagrant box add digital_ocean https://github.com/smdahlen/vagrant-digitalocean/raw/master/box/digital_ocean.box') end - all_providers = Dir["test/acceptance/*"].map{|dir| File.basename(File.expand_path(dir))} + all_providers = Dir['test/acceptance/*'].map do |dir| + File.basename(File.expand_path(dir)) + end # If a provider wasn't passed to the task run acceptance tests against # ALL THE PROVIDERS! @@ -38,14 +51,14 @@ namespace :test do end providers.each do |provider| - puts "==================================================================" + puts '==================================================================' puts "Running acceptance tests against '#{provider}' provider..." - puts "==================================================================" + puts '==================================================================' Dir.chdir("test/acceptance/#{provider}") do - system("vagrant destroy -f") + system('vagrant destroy -f') system("vagrant up --provider=#{provider}") - system("vagrant destroy -f") + system('vagrant destroy -f') end end end diff --git a/lib/vagrant-omnibus/action/install_chef.rb b/lib/vagrant-omnibus/action/install_chef.rb index 0c7a875..3103602 100644 --- a/lib/vagrant-omnibus/action/install_chef.rb +++ b/lib/vagrant-omnibus/action/install_chef.rb @@ -26,24 +26,19 @@ module Action # # This action installs Chef Omnibus packages at the desired version. class InstallChef - INSTALL_SH = "#{ENV['OMNIBUS_INSTALL_URL'] || - 'https://www.opscode.com/chef/install.sh'}" - def initialize(app, env) @app = app @logger = Log4r::Logger.new('vagrantplugins::omnibus::action::installchef') @machine = env[:machine] - # Config#finalize! SHOULD be called automatically + @install_script = find_install_script @machine.config.omnibus.finalize! end def call(env) @app.call(env) - return if !@machine.communicate.ready? || - !provision_enabled?(env) || - windows_guest?(env) + return unless @machine.communicate.ready? && provision_enabled?(env) desired_version = @machine.config.omnibus.chef_version unless desired_version.nil? @@ -53,7 +48,7 @@ def call(env) version: desired_version ) else - fetch_install_sh(env) + fetch_or_create_install_script(env) env[:ui].info I18n.t( 'vagrant-omnibus.action.installing', version: desired_version @@ -66,8 +61,28 @@ def call(env) private - def windows_guest?(env) - env[:machine].config.vm.guest.eql?(:windows) + # Determines what flavor of install script should be used to + # install Omnibus Chef package. + def find_install_script + if !ENV['OMNIBUS_INSTALL_URL'].nil? + ENV['OMNIBUS_INSTALL_URL'] + elsif windows_guest? + 'http://www.getchef.com/chef/install.msi' + else + 'https://www.getchef.com/chef/install.sh' + end + end + + def install_script_name + if windows_guest? + 'install.bat' + else + 'install.sh' + end + end + + def windows_guest? + @machine.config.vm.guest.eql?(:windows) end def provision_enabled?(env) @@ -76,8 +91,14 @@ def provision_enabled?(env) def installed_version version = nil - command = 'echo $(chef-solo -v)' - @machine.communicate.sudo(command) do |type, data| + opts = nil + if windows_guest? + command = 'cmd.exe /c chef-solo -v 2>&0' + opts = { shell: :cmd, error_check: false } + else + command = 'echo $(chef-solo -v)' + end + @machine.communicate.sudo(command, opts) do |type, data| if [:stderr, :stdout].include?(type) next if data =~ /stdin: is not a tty/ v = data.chomp @@ -87,15 +108,22 @@ def installed_version version end - # Uploads install.sh from Host's Vagrant TMP directory to guest + # + # Upload install script from Host's Vagrant TMP directory to guest # and executes. + # def install(version, env) shell_escaped_version = Shellwords.escape(version) @machine.communicate.tap do |comm| - comm.upload(@install_sh_temp_path, 'install.sh') - # TODO: Execute with `sh` once install.sh removes it's bash-isms. - install_cmd = "bash install.sh -v #{shell_escaped_version} 2>&1" + comm.upload(@script_tmp_path, install_script_name) + if windows_guest? + install_cmd = "cmd.exe /c #{install_script_name} #{version}" + else + # TODO: Execute with `sh` once install.sh removes it's bash-isms. + install_cmd = + "bash #{install_script_name} -v #{shell_escaped_version} 2>&1" + end comm.sudo(install_cmd) do |type, data| if [:stderr, :stdout].include?(type) next if data =~ /stdin: is not a tty/ @@ -105,18 +133,18 @@ def install(version, env) end end - # Fetches install.sh file to the Host's Vagrant TMP directory. - # - # Mostly lifted from: # - # mitchellh/vagrant/blob/master/lib/vagrant/action/builtin/box_add.rb + # Fetches or creates a platform specific install script to the Host's + # Vagrant TMP directory. # - def fetch_install_sh(env) - @install_sh_temp_path = - env[:tmp_path].join(Time.now.to_i.to_s + '-install.sh') - @logger.info("Downloading install.sh to: #{@install_sh_temp_path}") + def fetch_or_create_install_script(env) + @script_tmp_path = + env[:tmp_path].join("#{Time.now.to_i.to_s}-#{install_script_name}") + + @logger.info("Generating install script at: #{@script_tmp_path}") + + url = @install_script - url = INSTALL_SH if File.file?(url) || url !~ /^[a-z0-9]+:.*$/i @logger.info('Assuming URL is a file.') file_path = File.expand_path(url) @@ -124,18 +152,37 @@ def fetch_install_sh(env) url = "file:#{file_path}" end - downloader_options = {} - # downloader_options[:insecure] = env[:box_download_insecure] - # downloader_options[:ui] = env[:ui] - - # Download the install.sh file to a temporary path. We store - # the temporary path as an instance variable so that the - # `#recover` method can access it. + # Download the install.sh or create install.bat file to a temporary + # path. We store the temporary path as an instance variable so that + # the `#recover` method can access it. begin - downloader = Vagrant::Util::Downloader.new(url, - @install_sh_temp_path, - downloader_options) - downloader.download! + if windows_guest? + # generate a install.bat file at the `@script_tmp_path` location + # + # We'll also disable Rubocop for this embedded PowerShell code: + # + # rubocop:disable LineLength, SpaceAroundBlockBraces + # + File.open(@script_tmp_path, 'w') do |f| + f.puts <<-EOH.gsub(/^\s{18}/, '') + @echo off + set version=%1 + set dest=%~dp0chef-client-%version%-1.windows.msi + echo Downloading Chef %version% for Windows... + powershell -command "(New-Object System.Net.WebClient).DownloadFile('#{url}?v=%version%', '%dest%')" + echo Installing Chef %version% + msiexec /q /i %dest% + EOH + end + # rubocop:enable LineLength, SpaceAroundBlockBraces + else + downloader = Vagrant::Util::Downloader.new( + url, + @script_tmp_path, + {} + ) + downloader.download! + end rescue Vagrant::Errors::DownloaderInterrupted # The downloader was interrupted, so just return, because that # means we were interrupted as well. @@ -145,8 +192,8 @@ def fetch_install_sh(env) end def recover(env) - if @install_sh_temp_path && File.exist?(@install_sh_temp_path) - File.unlink(@install_sh_temp_path) + if @script_tmp_path && File.exist?(@script_tmp_path) + File.unlink(@script_tmp_path) end end end diff --git a/lib/vagrant-omnibus/config.rb b/lib/vagrant-omnibus/config.rb index 0e55d52..4cbfe58 100644 --- a/lib/vagrant-omnibus/config.rb +++ b/lib/vagrant-omnibus/config.rb @@ -46,7 +46,7 @@ def validate(machine) unless valid_chef_version?(chef_version) msg = <<-EOH -'#{chef_version}' is not a valid version of Chef. +'#{ chef_version }' is not a valid version of Chef. A list of valid versions can be found at: http://www.opscode.com/chef/install/ EOH diff --git a/test/acceptance/aws/Vagrantfile b/test/acceptance/aws/Vagrantfile index cb23810..fe56c42 100644 --- a/test/acceptance/aws/Vagrantfile +++ b/test/acceptance/aws/Vagrantfile @@ -5,24 +5,25 @@ require 'vagrant-omnibus' require 'vagrant-aws' -Vagrant.configure("2") do |config| - config.omnibus.chef_version = "11.4.0" +Vagrant.configure('2') do |config| + config.omnibus.chef_version = '11.4.0' - config.vm.box = "dummy" + config.vm.box = 'dummy' config.vm.provider :aws do |aws, override| aws.access_key_id = ENV['DEV_AWS_ACCESS_KEY_ID'] aws.secret_access_key = ENV['DEV_AWS_SECRET_ACCESS_KEY'] aws.keypair_name = ENV['USER'] - aws.instance_type = "m1.large" - aws.ami = "ami-2efa9d47" # Ubuntu 12.04 LTS 64-bit instance root store + aws.instance_type = 'm1.large' + aws.ami = 'ami-2efa9d47' # Ubuntu 12.04 LTS 64-bit instance root store - override.ssh.username = "ubuntu" - override.ssh.private_key_path = "~/.ssh/id_rsa" + override.ssh.username = 'ubuntu' + override.ssh.private_key_path = '~/.ssh/id_rsa' end config.vm.provision :chef_solo do |chef| - chef.cookbooks_path = File.expand_path("../../../support/cookbooks", __FILE__) - chef.add_recipe "chef-inator" + chef.cookbooks_path = + File.expand_path('../../../support/cookbooks', __FILE__) + chef.add_recipe 'chef-inator' end end diff --git a/test/acceptance/digital_ocean/Vagrantfile b/test/acceptance/digital_ocean/Vagrantfile index 69a26b6..30093d8 100644 --- a/test/acceptance/digital_ocean/Vagrantfile +++ b/test/acceptance/digital_ocean/Vagrantfile @@ -5,7 +5,7 @@ Vagrant.configure('2') do |config| config.omnibus.chef_version = :latest config.vm.box = 'digital_ocean' - config.vm.synced_folder '.', '/vagrant', :disabled => true + config.vm.synced_folder '.', '/vagrant', disabled: true config.vm.provider :digital_ocean do |provider, override| provider.client_id = ENV['DO_CLIENT_ID'] @@ -17,7 +17,8 @@ Vagrant.configure('2') do |config| end config.vm.provision :chef_solo do |chef| - chef.cookbooks_path = File.expand_path("../../../support/cookbooks", __FILE__) - chef.add_recipe "chef-inator" + chef.cookbooks_path = + File.expand_path('../../../support/cookbooks', __FILE__) + chef.add_recipe 'chef-inator' end end diff --git a/test/acceptance/rackspace/Vagrantfile b/test/acceptance/rackspace/Vagrantfile index 7cf5262..27fb4ce 100644 --- a/test/acceptance/rackspace/Vagrantfile +++ b/test/acceptance/rackspace/Vagrantfile @@ -5,23 +5,24 @@ require 'vagrant-omnibus' require 'vagrant-rackspace' -Vagrant.configure("2") do |config| +Vagrant.configure('2') do |config| config.omnibus.chef_version = '11.4.0' - config.vm.box = "dummy" + config.vm.box = 'dummy' config.vm.provider :rackspace do |rackspace| rackspace.username = ENV['DEV_RACKSPACE_USERNAME'] rackspace.api_key = ENV['DEV_RACKSPACE_API_KEY'] rackspace.flavor = /512MB/ rackspace.image = /Ubuntu 12.04/ - rackspace.public_key_path = "~/.ssh/id_rsa.pub" - # TODO - switch this to the `override.ssh.private_key_path` syntax once + rackspace.public_key_path = '~/.ssh/id_rsa.pub' + # TODO: switch this to the `override.ssh.private_key_path` syntax once # `vagrant-rackspace` is updated. - config.ssh.private_key_path = "~/.ssh/id_rsa" + config.ssh.private_key_path = '~/.ssh/id_rsa' end config.vm.provision :chef_solo do |chef| - chef.cookbooks_path = File.expand_path("../../../support/cookbooks", __FILE__) - chef.add_recipe "chef-inator" + chef.cookbooks_path = + File.expand_path('../../../support/cookbooks', __FILE__) + chef.add_recipe 'chef-inator' end end diff --git a/test/acceptance/virtualbox/Vagrantfile b/test/acceptance/virtualbox/Vagrantfile index d064496..847795b 100644 --- a/test/acceptance/virtualbox/Vagrantfile +++ b/test/acceptance/virtualbox/Vagrantfile @@ -1,32 +1,35 @@ # -*- mode: ruby -*- # vi: set ft=ruby : +# rubocop:disable LineLength + # plugins don't seem to be auto-loaded from the bundle require 'vagrant-omnibus' +require 'vagrant-windows' -Vagrant.configure("2") do |config| +Vagrant.configure('2') do |config| config.vm.define :new_chef do |new_chef_config| - new_chef_config.vm.box = "opscode-ubuntu-12.04" - new_chef_config.vm.box_url = "http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_ubuntu-12.04_chef-provisionerless.box" + new_chef_config.vm.box = 'opscode-ubuntu-12.04' + new_chef_config.vm.box_url = 'http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_ubuntu-12.04_chef-provisionerless.box' new_chef_config.omnibus.chef_version = :latest new_chef_config.vm.provision :chef_solo do |chef| - chef.cookbooks_path = File.expand_path("../../../support/cookbooks", __FILE__) - chef.add_recipe "chef-inator" + chef.cookbooks_path = File.expand_path('../../../support/cookbooks', __FILE__) + chef.add_recipe 'chef-inator' end end config.vm.define :old_chef do |old_chef_config| - old_chef_config.vm.box = "opscode-centos-6.5" - old_chef_config.vm.box_url = "http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-6.5_chef-provisionerless.box" + old_chef_config.vm.box = 'opscode-centos-6.5' + old_chef_config.vm.box_url = 'http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-6.5_chef-provisionerless.box' old_chef_config.omnibus.chef_version = '10.24.0' old_chef_config.vm.provision :chef_solo do |chef| - chef.cookbooks_path = File.expand_path("../../../support/cookbooks", __FILE__) - chef.add_recipe "chef-inator" + chef.cookbooks_path = File.expand_path('../../../support/cookbooks', __FILE__) + chef.add_recipe 'chef-inator' end end @@ -37,8 +40,27 @@ Vagrant.configure("2") do |config| installed_config.omnibus.chef_version = '11.4.4' installed_config.vm.provision :chef_solo do |chef| - chef.cookbooks_path = File.expand_path("../../../support/cookbooks", __FILE__) - chef.add_recipe "chef-inator" + chef.cookbooks_path = File.expand_path('../../../support/cookbooks', __FILE__) + chef.add_recipe 'chef-inator' + end + end + + config.vm.define :chef_on_windows do |win_config| + win_config.vm.box = 'opscode-windows-6.1' + win_config.vm.box_url = 'http://opscode-vm.s3.amazonaws.com/vagrant/boxes/opscode-windows-6.1.box' + win_config.vm.boot_timeout = 500 + + win_config.omnibus.chef_version = :latest + + win_config.vm.network :forwarded_port, guest: 3389, host: 3389 + win_config.vm.network :forwarded_port, guest: 5985, host: 5985 + + win_config.vm.guest = :windows + win_config.winrm.timeout = 500 + + win_config.vm.provision :chef_solo do |chef| + chef.cookbooks_path = File.expand_path('../../../support/cookbooks', __FILE__) + chef.add_recipe 'chef-inator' end end end diff --git a/vagrant-omnibus.gemspec b/vagrant-omnibus.gemspec index 01cbcf3..105d543 100644 --- a/vagrant-omnibus.gemspec +++ b/vagrant-omnibus.gemspec @@ -4,22 +4,24 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'vagrant-omnibus/version' Gem::Specification.new do |spec| - spec.name = "vagrant-omnibus" + spec.name = 'vagrant-omnibus' spec.version = VagrantPlugins::Omnibus::VERSION - spec.authors = ["Seth Chisamore"] - spec.email = ["schisamo@opscode.com"] - spec.description = %q{A Vagrant plugin that ensures the desired version of Chef is installed via the platform-specific Omnibus packages.} + spec.authors = ['Seth Chisamore'] + spec.email = ['schisamo@opscode.com'] + spec.description = 'A Vagrant plugin that ensures the desired version of' \ + ' Chef is installed via the platform-specific Omnibus' \ + ' packages.' spec.summary = spec.description - spec.homepage = "https://github.com/schisamo/vagrant-omnibus" - spec.license = "Apache 2.0" + spec.homepage = 'https://github.com/schisamo/vagrant-omnibus' + spec.license = 'Apache 2.0' - spec.files = `git ls-files`.split($/) - spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } - spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) - spec.require_paths = ["lib"] + spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR) + spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) } + spec.test_files = spec.files.grep(/^(test|spec|features)\//) + spec.require_paths = ['lib'] - spec.add_development_dependency "bundler", "~> 1.3" - spec.add_development_dependency "rake" - spec.add_development_dependency "rspec" + spec.add_development_dependency 'bundler', '~> 1.3' + spec.add_development_dependency 'rake' + spec.add_development_dependency 'rspec' spec.add_development_dependency 'rubocop', '~> 0.15.0' end