winnfsd / vagrant-winnfsd

Manage and adds support for NFS for Vagrant on Windows.
Other
567 stars 62 forks source link

winnfsd incorrectly uses drive letter on host with an existing NFS server #91

Closed JakeTheSnake3p0 closed 7 years ago

JakeTheSnake3p0 commented 8 years ago

I initially thought that the vagrant-winnfsd plugin would take care of NFS storage on Windows (meaning I wouldn't have to use haneWIN) but that was a no-go because the error I received was mount.nfs: requested NFS version or transport protocol is not supported.

After re-activating haneWIN I now receive this error: mount.nfs: mounting 172.28.128.1:/D/git-repositories/+vm failed, reason given by server: No such file or directory.

I get the impression that the [URL]:[mount] format is incorrect because on another VM I manually set up my mount in fstab with the following: 192.168.11.2:/websites /home/vagrant/rails nfs nfsvers=3,vers=3,rsize=8192,wsize=8192,timeo=14,auto,intr,udp,nolock,exec,rw,user.

Should Vagrant's attempt not look something like 172.28.128.1:/websites given the following configuration?


Host OS: Windows 7 x64, LAN IP: 192.168.11.2
Guest OS: Ubuntu/trusty64
Virtualizer: Virtualbox 5.0.20 r106931
Relevant Plugins: vagrant-winnfsd
Host OS NFS Server: haneWIN NFS Server

NFS Server Exports: D:\git-repositories\+vm -name:websites


Vagrantfile excerpt:

# Create a private network, which allows host-only access to the machine
# using a specific IP.
config.vm.network :private_network, type: :dhcp, auto_config: false

# Create a public network, which generally matched to bridged network.
# Bridged networks make the machine appear as another physical device on
# your network.
config.vm.network :public_network,
                  ip: '192.168.11.14',
                  bridge: 'Realtek PCIe GBE Family Controller'

# Share an additional folder to the guest VM. The first argument is
# the path on the host to the actual folder. The second argument is
# the path on the guest to mount the folder. And the optional third
# argument is a set of non-required options.
#
# Do not share root directory of vagrant
config.vm.synced_folder '.', '/vagrant', disabled: true
# Share ruby repository directories
config.vm.synced_folder 'D:/git-repositories/+vm',
                        '/home/vagrant/apps',
                        nfs: true,
                        mount_options: [
                          'nfsvers=3',
                          'vers=3',
                          'actimeo=1',
                          'rsize=8192',
                          'wsize=8192',
                          'timeo=14',
                          :nolock,
                          :udp,
                          :intr,
                          :user,
                          :auto,
                          :exec,
                          :rw
                        ]
JakeTheSnake3p0 commented 8 years ago

The solution was to put the following into the exports file of haneWIN server:

D:\git-repositories\+vm -name:I

As my Vagrantfile is found in I:\Vagrantfile.

So essentially calls to map [whatever-ip-address]:/I would be successful. Then I changed my Vagrantfile to the following:

# Share ruby repository directories
config.vm.synced_folder '.',
                        '/home/vagrant/apps',
                        nfs: true,
                        mount_options: [
                          'nfsvers=3',
                          'vers=3',
                          'actimeo=1',
                          'rsize=8192',
                          'wsize=8192',
                          'timeo=14',
                          :nolock,
                          :udp,
                          :intr,
                          :user,
                          :auto,
                          :exec,
                          :rw
                        ]

So whatever winnfsd does to reference local files in exports, it's simply misconfigured. The user should be allowed to specify the exact path beyond the colon to properly reference the publicly accessible NFS export on the host.

marcharding commented 8 years ago

As far as i understand it, you want to use vagrant-winnfsd in combination with haneWIN because somehow winnfsd did not work? This will not (ever) work without manual intervention.

When using winnfsd.exe as a standalone executable you can specify the mount point (see https://github.com/winnfsd/winnfsd) just like with haneWIN, but when using the vagrant-winnfsd plugin this is not possible.

Your example config.vm.synced_folder 'D:/git-repositories/+vm', '/home/vagrant/apps', nfs: true however works (when not additionally using haneWIN). It will not be mounted via IP:/websites, but does that really matter after all?

marcharding commented 8 years ago

Hey, any update on this?

JakeTheSnake3p0 commented 8 years ago

So I stopped the "NFS Server" Windows Service (HaneWIN) and tried booting the VM but my apps directory was empty.

My new configuration - I had to disable the root directory share because I was getting an error:

There was an error while executing `VBoxManage`, a CLI used by Vagrant
for controlling VirtualBox. The command and stderr is shown below.

Command: ["sharedfolder", "add", "a8826719-a7e5-428c-9994-ae2fa8025d8b", "--name", "vagrant", "--hostpath", "I:

Stderr: VBoxManage.exe: error: Shared folder path 'I:"' is not absolute
VBoxManage.exe: error: Details: code E_INVALIDARG (0x80070057), component SharedFolderWrap, interface ISharedFo
lee IUnknown
VBoxManage.exe: error: Context: "CreateSharedFolder(Bstr(name).raw(), Bstr(hostpath).raw(), fWritable, fAutoMou
ine 1020 of file VBoxManageMisc.cpp

So here's the updated config:

# Do not share root directory of vagrant
  config.vm.synced_folder '.', '/vagrant', disabled: true
  # Share ruby repository directories
  config.vm.synced_folder 'D:/git-repositories/+vm',
                          '/home/vagrant/apps',
                          nfs: true,
                          mount_options: [
                            'nfsvers=3',
                            'vers=3',
                            'actimeo=1',
                            'rsize=8192',
                            'wsize=8192',
                            'timeo=14',
                            :nolock,
                            :udp,
                            :intr,
                            :user,
                            :auto,
                            :exec,
                            :rw
                          ]

This is the output of a normal vagrant up command, still resulting in an empty apps directory:

==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Remote connection disconnect. Retrying...
==> default: Machine booted and ready!
[default] GuestAdditions 5.0.20 running --- OK.
==> default: Checking for guest additions in VM...
==> default: Setting hostname...
==> default: Configuring and enabling network interfaces...
==> default: Exporting NFS shared folders...
==> default: Preparing to edit nfs mounting file.
[NFS] Status: halted
[NFS] Start: started
==> default: Mounting NFS shared folders...
==> default: Mounting shared folders...
    default: /tmp/vagrant-chef/0e0ecc321fcef117cce1464c621e4b9b/cookbooks => I:/cookbooks
==> default: Machine already provisioned. Run `vagrant provision` or use the `--provision`
==> default: flag to force provisioning. Provisioners marked to run always will still run.

There is no error output. So I booted with the --debug option - and after several quick-draw "Mark"s due to the excessive scrollback, I found the problem:

DEBUG ssh: stderr: mount.nfs: requested NFS version or transport protocol is not supported

This error persists regardless of what nfs version I set in the mount options.

FWIW I do NOT have winnfsd.exe set up as a standalone...but this aught to work regardless, no?

marcharding commented 8 years ago

Yes it should work and your configurations mounts for me without problems with ubuntu 14.04.

I'm quite sure that haneWIN NFS somehow conflicts with winnfsd/vagrant-winnfsd.

Can you completely remove haneWIN NFS and vagrant-winnfsd vagrant plugin uninstall vagrant-winnfsd, reboot your system, install vagrant-winnfsd and try again?

JakeTheSnake3p0 commented 8 years ago

On the first attempt after uninstalling/rebooting/reinstalling, the apps directory contained the contents of I:\ even though that was never specified in my config. After looking online at tangentially related solutions I tried converting / to \\ in my local directory reference.

Now I can't even ls the vagrant home directory or cd or ls the apps linked directory for that matter - it just hangs.

JakeTheSnake3p0 commented 8 years ago

Update: After removing all mount_options the directory loads, but all symlinked directories within it don't work. When I tried to cd into one of them it said directory does not exist. Also, when typing ls, it freezes. Perhaps the symlinks are the cause of most of these issues, but HaneWIN doesn't seem to mind the symlinks.

marcharding commented 8 years ago

I'll check whats the problem with your combination of mount options.

Symlinks only work if they are relative to the mounted nfs folders, you can only create symlinks if winnfsd is started from an elevated prompt.

Regarding the crashes: These should be fixed in the next release (currently alpha). You can try the binary linked here https://github.com/winnfsd/winnfsd/issues/22#issuecomment-232504404 (just replace the one in %userprofile%\.vagrant.d\gems\gems\vagrant-winnfsd-1.2.1\bin)

JakeTheSnake3p0 commented 8 years ago

Here is my host's folder layout:

marcharding commented 8 years ago

That won't work unless you mount the whole D:\git-repositories\ folder.

JakeTheSnake3p0 commented 8 years ago

Is there a way to have vagrant create symlinks after every successful nfs share creation instead of having to create it on the host machine? For example...

config.vm.synced_folder('D:\git-repositories\ruby', '/home/vagrant/', nfs: true) do |nfs|
   `ln -s ...`
end
JakeTheSnake3p0 commented 8 years ago

Manually putting the symlinks into the same directory as the NFS share fixed the problem, though I'm now getting Input/output error @ dir_s_rmdir - /home/vagrant/apps/pos/tmp... which after some Googling is most likely related to #22.

Update: Downloading the latest .exe you linked to doesn't solve the IO error. Hopefully I won't have to resort to a workaround.

Turning on logging doesn't seem to help as the only log line related to the error shown in rspec is this: NFS RMDIR \\?\D:\git-repositories\ruby\pos\tmp\uploads\1469230757-5222-0001-4444 IO. All other entries seem to end in OK or NOENT.

marcharding commented 8 years ago

Can you try mounting with TCP instead of UDP (with the latest exe)?

If the error persists, please provide me with a complete example on how to reproduce it. It would be great if you can narrow it down to a few lines of (ruby?)code that trigger the error.

JakeTheSnake3p0 commented 8 years ago

Mount options:

config.vm.synced_folder 'D:\git-repositories\ruby',
                        '/home/vagrant/apps',
                        nfs: true,
                        mount_options: [
                          'vers=3',
                          :nolock,
                          :tcp,
                          :intr,
                          :user,
                          :auto,
                          :exec,
                          :rw
                        ]

As for specific Ruby code, it would only be helpful to state that the error is produced by the carrierwave gem. I believe any basic usage of carrierwave storing an image would trigger it. Here's the stack trace:

Failure/Error: factory = FactoryGirl.build(factory_name, trait_name)

     Errno::EIO:
       Input/output error @ dir_s_rmdir - /home/vagrant/apps/pos/tmp/uploads/1470168440-5759-0001-4646
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/carrierwave-0.11.2/lib/carrierwave/uploader/store.rb:75:in `rmdir'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/carrierwave-0.11.2/lib/carrierwave/uploader/store.rb:75:in `delete_cache_id'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/carrierwave-0.11.2/lib/carrierwave/uploader/store.rb:61:in `block in store!'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/carrierwave-0.11.2/lib/carrierwave/uploader/callbacks.rb:17:in `with_callbacks'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/carrierwave-0.11.2/lib/carrierwave/uploader/store.rb:58:in `store!'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/carrierwave-0.11.2/lib/carrierwave/mount.rb:375:in `store!'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/carrierwave-0.11.2/lib/carrierwave/mount.rb:207:in `store_photo!'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/activesupport-4.2.7/lib/active_support/callbacks.rb:432:in `block in make_lambda'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/activesupport-4.2.7/lib/active_support/callbacks.rb:228:in `block in halting_and_conditional'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/activesupport-4.2.7/lib/active_support/callbacks.rb:506:in `block in call'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/activesupport-4.2.7/lib/active_support/callbacks.rb:506:in `each'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/activesupport-4.2.7/lib/active_support/callbacks.rb:506:in `call'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/activesupport-4.2.7/lib/active_support/callbacks.rb:92:in `__run_callbacks__'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/activesupport-4.2.7/lib/active_support/callbacks.rb:778:in `_run_save_callbacks'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/activerecord-4.2.7/lib/active_record/callbacks.rb:302:in `create_or_update'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/activerecord-4.2.7/lib/active_record/persistence.rb:142:in `save!'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/activerecord-4.2.7/lib/active_record/validations.rb:43:in `save!'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/activerecord-4.2.7/lib/active_record/attribute_methods/dirty.rb:29:in `save!'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/activerecord-4.2.7/lib/active_record/transactions.rb:291:in `block in save!'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/activerecord-4.2.7/lib/active_record/transactions.rb:351:in `block in with_transaction_returning_status'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/activerecord-4.2.7/lib/active_record/connection_adapters/abstract/database_statements.rb:213:in `block in transaction'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/activerecord-4.2.7/lib/active_record/connection_adapters/abstract/transaction.rb:184:in `within_new_transaction'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/activerecord-4.2.7/lib/active_record/connection_adapters/abstract/database_statements.rb:213:in `transaction'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/activerecord-4.2.7/lib/active_record/transactions.rb:220:in `transaction'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/activerecord-4.2.7/lib/active_record/transactions.rb:348:in `with_transaction_returning_status'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/activerecord-4.2.7/lib/active_record/transactions.rb:291:in `save!'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/factory_girl-4.7.0/lib/factory_girl/configuration.rb:18:in `block in initialize'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/factory_girl-4.7.0/lib/factory_girl/evaluation.rb:15:in `create'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/factory_girl-4.7.0/lib/factory_girl/strategy/create.rb:12:in `block in result'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/factory_girl-4.7.0/lib/factory_girl/strategy/create.rb:9:in `tap'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/factory_girl-4.7.0/lib/factory_girl/strategy/create.rb:9:in `result'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/factory_girl-4.7.0/lib/factory_girl/factory.rb:42:in `run'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/factory_girl-4.7.0/lib/factory_girl/factory_runner.rb:29:in `block in run'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/activesupport-4.2.7/lib/active_support/notifications.rb:166:in `instrument'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/factory_girl-4.7.0/lib/factory_girl/factory_runner.rb:28:in `run'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/factory_girl-4.7.0/lib/factory_girl/strategy/build.rb:5:in `association'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/factory_girl-4.7.0/lib/factory_girl/evaluator.rb:31:in `association'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/factory_girl-4.7.0/lib/factory_girl/attribute/association.rb:19:in `block in to_proc'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/factory_girl-4.7.0/lib/factory_girl/evaluator.rb:75:in `instance_exec'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/factory_girl-4.7.0/lib/factory_girl/evaluator.rb:75:in `block in define_attribute'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/factory_girl-4.7.0/lib/factory_girl/attribute_assigner.rb:56:in `get'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/factory_girl-4.7.0/lib/factory_girl/attribute_assigner.rb:16:in `block (2 levels) in object'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/factory_girl-4.7.0/lib/factory_girl/attribute_assigner.rb:15:in `each'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/factory_girl-4.7.0/lib/factory_girl/attribute_assigner.rb:15:in `block in object'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/factory_girl-4.7.0/lib/factory_girl/attribute_assigner.rb:14:in `tap'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/factory_girl-4.7.0/lib/factory_girl/attribute_assigner.rb:14:in `object'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/factory_girl-4.7.0/lib/factory_girl/evaluation.rb:12:in `object'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/factory_girl-4.7.0/lib/factory_girl/strategy/build.rb:9:in `result'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/factory_girl-4.7.0/lib/factory_girl/factory.rb:42:in `run'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/factory_girl-4.7.0/lib/factory_girl/factory_runner.rb:29:in `block in run'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/activesupport-4.2.7/lib/active_support/notifications.rb:166:in `instrument'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/factory_girl-4.7.0/lib/factory_girl/factory_runner.rb:28:in `run'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/factory_girl-4.7.0/lib/factory_girl/strategy_syntax_method_registrar.rb:20:in `block in define_singular_strategy_method'
     # ./spec/factories_spec.rb:20:in `block (6 levels) in <top (required)>'
     # ./spec/rails_helper.rb:48:in `block (3 levels) in <top (required)>'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/database_cleaner-1.5.3/lib/database_cleaner/generic/base.rb:16:in `cleaning'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/database_cleaner-1.5.3/lib/database_cleaner/base.rb:98:in `cleaning'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/database_cleaner-1.5.3/lib/database_cleaner/configuration.rb:86:in `block (2 levels) in cleaning'
     # /home/vagrant/.rvm/gems/ruby-2.3.1@pos/gems/database_cleaner-1.5.3/lib/database_cleaner/configuration.rb:87:in `cleaning'
     # ./spec/rails_helper.rb:47:in `block (2 levels) in <top (required)>'

Here's the source of the offending code:

# encoding: utf-8

module CarrierWave
  module Uploader
    module Store
      extend ActiveSupport::Concern

      include CarrierWave::Uploader::Callbacks
      include CarrierWave::Uploader::Configuration
      include CarrierWave::Uploader::Cache

      ##
      # Override this in your Uploader to change the filename.
      #
      # Be careful using record ids as filenames. If the filename is stored in the database
      # the record id will be nil when the filename is set. Don't use record ids unless you
      # understand this limitation.
      #
      # Do not use the version_name in the filename, as it will prevent versions from being
      # loaded correctly.
      #
      # === Returns
      #
      # [String] a filename
      #
      def filename
        @filename
      end

      ##
      # Calculates the path where the file should be stored. If +for_file+ is given, it will be
      # used as the filename, otherwise +CarrierWave::Uploader#filename+ is assumed.
      #
      # === Parameters
      #
      # [for_file (String)] name of the file <optional>
      #
      # === Returns
      #
      # [String] the store path
      #
      def store_path(for_file=filename)
        File.join([store_dir, full_filename(for_file)].compact)
      end

      ##
      # Stores the file by passing it to this Uploader's storage engine.
      #
      # If new_file is omitted, a previously cached file will be stored.
      #
      # === Parameters
      #
      # [new_file (File, IOString, Tempfile)] any kind of file object
      #
      def store!(new_file=nil)
        cache!(new_file) if new_file && ((@cache_id != parent_cache_id) || @cache_id.nil?)
        if @file and @cache_id
          with_callbacks(:store, new_file) do
            new_file = storage.store!(@file)
            @file.delete if (delete_tmp_file_after_storage && ! move_to_store)
            delete_cache_id
            @file = new_file
            @cache_id = nil
          end
        end
      end

      ##
      # Deletes a cache id (tmp dir in cache)
      #
      def delete_cache_id
        if @cache_id
          path = File.expand_path(File.join(cache_dir, @cache_id), CarrierWave.root)
          begin
            Dir.rmdir(path)
          rescue Errno::ENOENT
            # Ignore: path does not exist
          rescue Errno::ENOTDIR
            # Ignore: path is not a dir
          rescue Errno::ENOTEMPTY, Errno::EEXIST
            # Ignore: dir is not empty
          end
        end
      end

      ##
      # Retrieves the file from the storage.
      #
      # === Parameters
      #
      # [identifier (String)] uniquely identifies the file to retrieve
      #
      def retrieve_from_store!(identifier)
        with_callbacks(:retrieve_from_store, identifier) do
          @file = storage.retrieve!(identifier)
        end
      end

    private

      def full_filename(for_file)
        for_file
      end

      def storage
        @storage ||= self.class.storage.new(self)
      end

    end # Store
  end # Uploader
end # CarrierWave
marcharding commented 7 years ago

Hey can you try the attached binary, i think it may fix this.

WinNFSd.zip

(i also think #96 and #64 are related to this)

JakeTheSnake3p0 commented 7 years ago

A recent update of this gem solved the issue.