diff --git a/CHANGELOG.md b/CHANGELOG.md index c10ccc4..19ecaa2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,12 @@ -## [0.3.0](https://github.com/fgrehm/vagrant-cachier/compare/v0.2.0...master) (unreleased) +## [0.3.0](https://github.com/fgrehm/vagrant-cachier/compare/v0.2.0...v0.3.0) (Aug 5, 2013) + +BACKWARDS INCOMPATIBILITIES: + + - Machine scoped cache dirs are now kept on `.vagrant/machines/MACHINE/cache` + to allow downloaded packages to be reused between providers. If a single cache + directory exists, the plugin will automatically move it to the right place, + if multiple directories are found, it will halt execution and will error out, + letting the user know what has to be done in order to fix things. FEATURES: diff --git a/Gemfile.lock b/Gemfile.lock index 81c77db..d6d6cff 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -25,7 +25,7 @@ GIT PATH remote: . specs: - vagrant-cachier (0.3.0.dev) + vagrant-cachier (0.3.0) GEM remote: https://rubygems.org/ diff --git a/README.md b/README.md index 4aad8b2..c811008 100644 --- a/README.md +++ b/README.md @@ -36,10 +36,8 @@ For more information about available buckets, please see the [configuration sect * Vagrant's built in VirtualBox provider * [vagrant-lxc](https://github.com/fgrehm/vagrant-lxc) - -_It is possibly compatible with the [VMware providers](http://www.vagrantup.com/vmware) -as well but I haven't tried yet._ - +* [VMware providers](http://www.vagrantup.com/vmware) with NFS enabled (See + [GH-24](https://github.com/fgrehm/vagrant-cachier/issues/24) for more info) ## How does it work? diff --git a/lib/vagrant-cachier/action/clean.rb b/lib/vagrant-cachier/action/clean.rb index cd1fc0b..84702d0 100644 --- a/lib/vagrant-cachier/action/clean.rb +++ b/lib/vagrant-cachier/action/clean.rb @@ -11,7 +11,7 @@ module VagrantPlugins @env = env if env[:machine].state.id == :running && symlinks.any? - env[:ui].info 'Removing cache buckets symlinks...' + env[:ui].info I18n.t('vagrant_cachier.cleanup') symlinks.each do |symlink| remove_symlink symlink end @@ -37,4 +37,3 @@ module VagrantPlugins end end end - diff --git a/lib/vagrant-cachier/action/ensure_single_cache_root.rb b/lib/vagrant-cachier/action/ensure_single_cache_root.rb new file mode 100644 index 0000000..ae46b1e --- /dev/null +++ b/lib/vagrant-cachier/action/ensure_single_cache_root.rb @@ -0,0 +1,63 @@ +require_relative '../errors' + +module VagrantPlugins + module Cachier + class Action + class EnsureSingleCacheRoot + def initialize(app, env) + @app = app + end + + def call(env) + @env = env + + # If the cache is scoped to boxes or the existing cache dirs are not + # provider specific, there's nothing we need to do + if cache_scoped_to_machine? && provider_specific_cache_dirs.any? + ensure_single_cache_root_exists! + end + + @app.call(env) + end + + def cache_scoped_to_machine? + @env[:machine].config.cache.scope.to_sym == :machine + end + + def ensure_single_cache_root_exists! + if provider_specific_cache_dirs.size > 1 + cache_dirs = provider_specific_cache_dirs.map do |dir| + " - #{dir.to_s.gsub(/^#{@env[:root_path]}\//, '')}" + end + machine_path = @env[:machine].data_dir.parent.to_s.gsub(/^#{@env[:root_path]}\//, '') + raise Cachier::Errors::MultipleProviderSpecificCacheDirsFound, + machine: @env[:machine].name, + machine_path: machine_path, + dirs: cache_dirs.join("\n") + else + current_path = provider_specific_cache_dirs.first.to_s.gsub(/^#{@env[:root_path]}\//, '') + new_path = @env[:machine].data_dir.parent.join('cache') + FileUtils.rm_rf new_path.to_s if new_path.directory? + + new_path = new_path.to_s.gsub(/^#{@env[:root_path]}\//, '') + # If we got here there is a single provider specific cacher dir, so + # let's be nice with users and just fix it ;) + @env[:ui].warn I18n.t('vagrant_cachier.will_fix_machine_cache_dir', + current_path: current_path, + new_path: new_path) + FileUtils.mv current_path, new_path + end + end + + def provider_specific_cache_dirs + return @provider_specific_cache_dirs if @provider_specific_cache_dirs + + # By default data_dir points to ./.vagrant/machines//, + # so we go one directory up + machine_dir = @env[:machine].data_dir.parent + @provider_specific_cache_dirs = Pathname.glob(machine_dir.join('*/cache')) + end + end + end + end +end diff --git a/lib/vagrant-cachier/action/provision_ext.rb b/lib/vagrant-cachier/action/provision_ext.rb deleted file mode 100644 index d03a307..0000000 --- a/lib/vagrant-cachier/action/provision_ext.rb +++ /dev/null @@ -1,70 +0,0 @@ -require_relative '../bucket' - -module VagrantPlugins - module Cachier - module Action - module ProvisionExt - def self.included(base) - base.class_eval do - def cachier_debug(msg) - @logger.debug "[CACHIER] #{msg}" - end - - alias :old_call :call - def call(env) - return old_call(env) unless env[:machine].config.cache.enabled? - - @env = env - - FileUtils.mkdir_p(cache_root.to_s) unless cache_root.exist? - - nfs_flag = env[:machine].config.cache.enable_nfs - env[:machine].config.vm.synced_folder cache_root, '/tmp/vagrant-cache', id: "vagrant-cache", nfs: nfs_flag - - env[:cache_dirs] = [] - - old_call(env) - - configure_cache_buckets - end - - alias :old_run_provisioner :run_provisioner - def run_provisioner(*args) - configure_cache_buckets - old_run_provisioner(*args) - end - - def configure_cache_buckets - if @env[:machine].config.cache.auto_detect - Bucket.auto_detect(@env) - end - - return unless @env[:machine].config.cache.buckets.any? - - @env[:ui].info 'Configuring cache buckets...' - cache_config = @env[:machine].config.cache - cache_config.buckets.each do |bucket_name, configs| - cachier_debug "Installing #{bucket_name} with configs #{configs.inspect}" - Bucket.install(bucket_name, @env, configs) - end - - data_file = @env[:machine].data_dir.join('cache_dirs') - data_file.open('w') { |f| f.print @env[:cache_dirs].uniq.join("\n") } - end - - def cache_root - @cache_root ||= case @env[:machine].config.cache.scope - when :box - @env[:home_path].join('cache', @env[:machine].box.name) - when :machine - @env[:machine].data_dir.join('cache') - else - raise "Unknown cache scope: '#{@env[:machine].config.cache.scope}'" - end - end - end - end - end - end - end -end diff --git a/lib/vagrant-cachier/bucket/apt.rb b/lib/vagrant-cachier/bucket/apt.rb index f7f9165..d52219b 100644 --- a/lib/vagrant-cachier/bucket/apt.rb +++ b/lib/vagrant-cachier/bucket/apt.rb @@ -24,7 +24,7 @@ module VagrantPlugins end end else - @env[:ui].info "Skipping APT cache bucket as the guest machine does not support it" + @env[:ui].info I18n.t('vagrant_cachier.skipping_bucket', bucket: 'APT') end end end diff --git a/lib/vagrant-cachier/bucket/chef.rb b/lib/vagrant-cachier/bucket/chef.rb index 0ebe7df..b78bbe4 100644 --- a/lib/vagrant-cachier/bucket/chef.rb +++ b/lib/vagrant-cachier/bucket/chef.rb @@ -24,7 +24,7 @@ module VagrantPlugins end end else - @env[:ui].info "Skipping Chef cache bucket as the guest machine does not support it" + @env[:ui].info I18n.t('vagrant_cachier.skipping_bucket', bucket: 'Chef') end end end diff --git a/lib/vagrant-cachier/bucket/gem.rb b/lib/vagrant-cachier/bucket/gem.rb index 4919bb1..e166520 100644 --- a/lib/vagrant-cachier/bucket/gem.rb +++ b/lib/vagrant-cachier/bucket/gem.rb @@ -29,7 +29,7 @@ module VagrantPlugins end end else - @env[:ui].info "Skipping RubyGems cache bucket as the guest machine does not support it" + @env[:ui].info I18n.t('vagrant_cachier.skipping_bucket', bucket: 'RubyGems') end end end diff --git a/lib/vagrant-cachier/bucket/pacman.rb b/lib/vagrant-cachier/bucket/pacman.rb index 7b048f2..c9d9cdc 100644 --- a/lib/vagrant-cachier/bucket/pacman.rb +++ b/lib/vagrant-cachier/bucket/pacman.rb @@ -24,7 +24,7 @@ module VagrantPlugins end end else - @env[:ui].info "Skipping Pacman cache bucket as the guest machine does not support it" + @env[:ui].info I18n.t('vagrant_cachier.skipping_bucket', bucket: 'Pacman') end end end diff --git a/lib/vagrant-cachier/bucket/rvm.rb b/lib/vagrant-cachier/bucket/rvm.rb index b584b86..ce57ff8 100644 --- a/lib/vagrant-cachier/bucket/rvm.rb +++ b/lib/vagrant-cachier/bucket/rvm.rb @@ -29,7 +29,7 @@ module VagrantPlugins end end else - @env[:ui].info "Skipping RVM cache bucket as the guest machine does not support it" + @env[:ui].info I18n.t('vagrant_cachier.skipping_bucket', bucket: 'RVM') end end end diff --git a/lib/vagrant-cachier/bucket/yum.rb b/lib/vagrant-cachier/bucket/yum.rb index 3665d57..d43af7c 100644 --- a/lib/vagrant-cachier/bucket/yum.rb +++ b/lib/vagrant-cachier/bucket/yum.rb @@ -27,7 +27,7 @@ module VagrantPlugins end end else - @env[:ui].info "Skipping Yum cache bucket as the guest machine does not support it" + @env[:ui].info I18n.t('vagrant_cachier.skipping_bucket', bucket: 'Yum') end end end diff --git a/lib/vagrant-cachier/config.rb b/lib/vagrant-cachier/config.rb index b3fcda0..a450505 100644 --- a/lib/vagrant-cachier/config.rb +++ b/lib/vagrant-cachier/config.rb @@ -4,6 +4,8 @@ module VagrantPlugins attr_accessor :scope, :auto_detect, :enable_nfs attr_reader :buckets + ALLOWED_SCOPES = %w( box machine ) + def initialize @scope = UNSET_VALUE @auto_detect = UNSET_VALUE @@ -14,6 +16,18 @@ module VagrantPlugins (@buckets ||= {})[bucket] = opts end + def validate(machine) + errors = _detected_errors + + if enabled? && ! ALLOWED_SCOPES.include?(@scope.to_s) + errors << I18n.t('vagrant_cachier.unknown_cache_scope', + allowed: ALLOWED_SCOPES.inspect, + cache_scope: @scope) + end + + { "vagrant cachier" => errors } + end + def finalize! return unless enabled? diff --git a/lib/vagrant-cachier/errors.rb b/lib/vagrant-cachier/errors.rb new file mode 100644 index 0000000..0f11696 --- /dev/null +++ b/lib/vagrant-cachier/errors.rb @@ -0,0 +1,9 @@ +module VagrantPlugins + module Cachier + module Errors + class MultipleProviderSpecificCacheDirsFound < Vagrant::Errors::VagrantError + error_key(:multiple_provider_specific_cache_dirs_found) + end + end + end +end diff --git a/lib/vagrant-cachier/plugin.rb b/lib/vagrant-cachier/plugin.rb index c337090..5625ff8 100644 --- a/lib/vagrant-cachier/plugin.rb +++ b/lib/vagrant-cachier/plugin.rb @@ -1,8 +1,11 @@ -require_relative 'action/provision_ext' +require_relative 'provision_ext' Vagrant::Action::Builtin::Provision.class_eval do - include VagrantPlugins::Cachier::Action::ProvisionExt + include VagrantPlugins::Cachier::ProvisionExt end +# Add our custom translations to the load path +I18n.load_path << File.expand_path("../../../locales/en.yml", __FILE__) + module VagrantPlugins module Cachier class Plugin < Vagrant.plugin('2') @@ -43,12 +46,27 @@ module VagrantPlugins Cap::Arch::PacmanCacheDir end + # TODO: This should be generic, we don't want to hard code every single + # possible provider action class that Vagrant might have + ensure_single_cache_root = lambda do |hook| + require_relative 'action/ensure_single_cache_root' + hook.before VagrantPlugins::ProviderVirtualBox::Action::Boot, Action::EnsureSingleCacheRoot + + if defined?(Vagrant::LXC) + # TODO: Require just the boot action file once its "require dependencies" are sorted out + require 'vagrant-lxc/action' + hook.before Vagrant::LXC::Action::Boot, Action::EnsureSingleCacheRoot + end + end + action_hook 'ensure-single-cache-root-exists-on-up', :machine_action_up, &ensure_single_cache_root + action_hook 'ensure-single-cache-root-exists-on-reload', :machine_action_reload, &ensure_single_cache_root + clean_action_hook = lambda do |hook| require_relative 'action/clean' - hook.before Vagrant::Action::Builtin::GracefulHalt, VagrantPlugins::Cachier::Action::Clean + hook.before Vagrant::Action::Builtin::GracefulHalt, Action::Clean end - action_hook 'remove-guest-symlinks-on-machine-halt', :machine_action_halt, &clean_action_hook - action_hook 'remove-guest-symlinks-on-machine-package', :machine_action_package, &clean_action_hook + action_hook 'remove-guest-symlinks-on-halt', :machine_action_halt, &clean_action_hook + action_hook 'remove-guest-symlinks-on-package', :machine_action_package, &clean_action_hook end end end diff --git a/lib/vagrant-cachier/provision_ext.rb b/lib/vagrant-cachier/provision_ext.rb new file mode 100644 index 0000000..21e094e --- /dev/null +++ b/lib/vagrant-cachier/provision_ext.rb @@ -0,0 +1,68 @@ +require_relative 'bucket' + +module VagrantPlugins + module Cachier + module ProvisionExt + def self.included(base) + base.class_eval do + def cachier_debug(msg) + @logger.debug "[CACHIER] #{msg}" + end + + alias :old_call :call + def call(env) + return old_call(env) unless env[:machine].config.cache.enabled? + + @env = env + + FileUtils.mkdir_p(cache_root.to_s) unless cache_root.exist? + + nfs_flag = env[:machine].config.cache.enable_nfs + env[:machine].config.vm.synced_folder cache_root, '/tmp/vagrant-cache', id: "vagrant-cache", nfs: nfs_flag + + env[:cache_dirs] = [] + + old_call(env) + + configure_cache_buckets + end + + alias :old_run_provisioner :run_provisioner + def run_provisioner(*args) + configure_cache_buckets + old_run_provisioner(*args) + end + + def configure_cache_buckets + if @env[:machine].config.cache.auto_detect + Bucket.auto_detect(@env) + end + + return unless @env[:machine].config.cache.buckets.any? + + @env[:ui].info 'Configuring cache buckets...' + cache_config = @env[:machine].config.cache + cache_config.buckets.each do |bucket_name, configs| + cachier_debug "Installing #{bucket_name} with configs #{configs.inspect}" + Bucket.install(bucket_name, @env, configs) + end + + data_file = @env[:machine].data_dir.join('cache_dirs') + data_file.open('w') { |f| f.print @env[:cache_dirs].uniq.join("\n") } + end + + def cache_root + @cache_root ||= case @env[:machine].config.cache.scope.to_sym + when :box + @env[:home_path].join('cache', @env[:machine].box.name) + when :machine + @env[:machine].data_dir.parent.join('cache') + else + raise "Unknown cache scope: '#{@env[:machine].config.cache.scope}'" + end + end + end + end + end + end +end diff --git a/lib/vagrant-cachier/version.rb b/lib/vagrant-cachier/version.rb index e892e3f..ee7cfba 100644 --- a/lib/vagrant-cachier/version.rb +++ b/lib/vagrant-cachier/version.rb @@ -1,5 +1,5 @@ module VagrantPlugins module Cachier - VERSION = "0.3.0.dev" + VERSION = "0.3.0" end end diff --git a/locales/en.yml b/locales/en.yml new file mode 100644 index 0000000..b54004d --- /dev/null +++ b/locales/en.yml @@ -0,0 +1,20 @@ +en: + vagrant_cachier: + cleanup: |- + Removing cache buckets symlinks... + skipping_bucket: |- + Skipping %{bucket} cache bucket as the guest machine does not support it + unknown_cache_scope: |- + Unknown cache scope '%{cache_scope}' (allowed scopes: %{allowed}) + will_fix_machine_cache_dir: |- + A vagrant-cachier provider specific cache dir was found under + '%{current_path}' and it will be moved to + '%{new_path}' as it is the new path for keeping machine + scoped cache dirs starting with the 0.3.0 version of the plugin. + vagrant: + errors: + multiple_provider_specific_cache_dirs_found: |- + There are multiple provider specific cache dirs for the '%{machine}' machine: + %{dirs} + Please move one of them up to `%{machine_path}/cache` and remove the others + before bringing the machine up again.