Bart van Heukelom
Bart van Heukelom

Reputation: 44114

Check for existing directory fails in Ruby + Chef

This is my piece of Ruby in a Chef recipe:

# if datadir doesn't exist, move over the default one
if !File.exist?("/vol/postgres/data")
    execute "mv /var/lib/postgresql/9.1/main /vol/postgres/data"
end

The result is:

Executing mv /var/lib/postgresql/9.1/main /vol/postgres/data
mv: inter-device move failed: `/var/lib/postgresql/9.1/main' to `/vol/postgres/data/main'; unable to remove target: Is a directory

I know that /vol/postgres/data exists and is a directory, yet it still attempts to execute the mv. Why?

Just to be sure, running the following standalone Ruby script on the same machine outputs "nomv":

if !File.exist?("/vol/postgres/data")
print "mv"
else
print "nomv"
end

Upvotes: 5

Views: 21184

Answers (7)

David Ferenczy Rogožan
David Ferenczy Rogožan

Reputation: 25451

To test if a directory exists you can use an equvivalent of File.exists which is Dir.exist:

Dir.exist?("/vol/postgres/data")

As others pointed out, you should use not_if or only_if instead of using plain Ruby condition, so I'm not going to explain it again. Check Draco's answer for details.

execute "mv /var/lib/postgresql/9.1/main /vol/postgres/data" do
  not_if { Dir.exist?("/vol/postgres/data") }
end

Upvotes: 1

Rajesh Hegde
Rajesh Hegde

Reputation: 2822

Use this block of code :

execute "name" do
    command "mv /var/lib/postgresql/9.1/main /vol/postgres/data"
    not_if { ::File.exists?("/vol/postgres/data")}
end

OR

you can also use

execute "name" do
    command "mv /var/lib/postgresql/9.1/main /vol/postgres/data"
    creates "/vol/postgres/data"
end

Both will run the command only if /vol/postgres/data is not present in the file system. If you want to run block of commands then use something like this,

bash 'name' do
  not_if { ::File.exists?("/vol/postgres/data") }
  cwd "/"
  code <<-EOH
  mv /var/lib/postgresql/9.1/main /vol/postgres/data
  #any other bash commands 
  #any other bash commands
  EOH
end

Upvotes: 5

Robert
Robert

Reputation: 10953

I use

!::File.directory?(::File.join('path/to/directory', 'directory_name'))

Upvotes: 1

Draco Ater
Draco Ater

Reputation: 21226

I was not so attentive earlier, I thought you are checking for file existence in not_if or only_if block. Your problem is similar to the one in this question: Chef LWRP - defs/resources execution order. See the detailed explanation there.

Your problem is that !File.exist?("/vol/postgres/data") code gets executed straight away - (because it's pure ruby), before any resource is executed and thus before the postgress is installed.

The solution should be to move the check to not_if block.

execute "mv /var/lib/postgresql/9.1/main /vol/postgres/data" do
  not_if { File.exist?("/vol/postgres/data") }
end

Upvotes: 9

DGM
DGM

Reputation: 26979

A quick google search turns up a lot of answers regarding "inter-device move failed". Ruby is just passing along the error returned by the operating system; this has nothing to do with testing the file as the other answers indicate.

From: http://insanelabs.com/linux/linux-cannot-move-folders-inter-device-move-failed-unable-to-remove-target-is-a-directory/

This is somewhat simple as long as we understand the concept. mv or move does not actually move the file/folder to another location within the same device, it merely replaces the pointer in the first sector of your device. The pointer (in inode table) will be moved, but nothing is actually being copied. This will work as long as you stay within the same media/device.

Now, when you try to move files from one device to another (/dev/sda1 to /dev/sdb1) you will run into “inter-device move failed, unable to remove target: Is a directory” error. This happens when mv has to actually move your data to another device, but cannot remove the inode/pointer, because if it did then there will be no data to fall back to, and if it didn’t then mv operation is not really complete because we will end up with data in source. Damned if you do and damned if you don’t, so it’s wise not to do it to begin with!

In such situation cp is best. Copy your data over and then remove your source manually.

A better solution might be to just use ruby tools instead of executing a shell command, since it says If file and dest exist on the different disk partition, the file is copied then the original file is removed.

FileUtils.mv '/var/lib/postgresql/9.1/main', '/vol/postgres/data'

Upvotes: 0

Amit
Amit

Reputation: 224

Are you calling it within your rails application or it is a standalone ruby file.

If you are doing in your rails app.

Then,

File.exist?("#{Rails.root}/ur-file-path")

Ex: File.exist?("#{Rails.root}/public/ur-filename")

You need to specify the particular file path from root.

Upvotes: 0

beck03076
beck03076

Reputation: 3308

I would use, !File.directory?("/vol/postgres/data")

Upvotes: 0

Related Questions