Migrate active storage from local disk service to gcs cloud - ruby-on-rails

I'm trying to migrate my local Active Storage files to Google Cloud Storage. I tried to just copy the files of /storage/* to my GCS Bucket - but it seems that this does not work.
I get 404 not found errors cause it is searching for files like:
[bucket]/variants/ptGtmNWuTE...
My local storage directory has a totally different folder structure with folders like:
/storage/1R/3o/NWuT....
My method to retrieve the image is as followed:
variant = attachment.variant(resize: '100x100').processed
url_for(variant)
What am i missing here?

As it turns out - DiskService aka. local storage uses a different folder structure than the cloud services. Thats really weird.
DiskService uses as folders part of the first chars of the key.
Cloud services just use the key and put all variants in a separate folder.
Created a rake task to copy files over to cloud services. Run it with rails active_storage:migrate_local_to_cloud storage_config=google for example.
namespace :active_storage do
desc "Migrates active storage local files to cloud"
task migrate_local_to_cloud: :environment do
raise 'Missing storage_config param' if !ENV.has_key?('storage_config')
require 'yaml'
require 'erb'
require 'google/cloud/storage'
config_file = Pathname.new(Rails.root.join('config/storage.yml'))
configs = YAML.load(ERB.new(config_file.read).result) || {}
config = configs[ENV['storage_config']]
client = Google::Cloud.storage(config['project'], config['credentials'])
bucket = client.bucket(config.fetch('bucket'))
ActiveStorage::Blob.find_each do |blob|
key = blob.key
folder = [key[0..1], key[2..3]].join('/')
file_path = Rails.root.join('storage', folder.to_s, key)
file = File.open(file_path, 'rb')
md5 = Digest::MD5.base64digest(file.read)
bucket.create_file(file, key, content_type: blob.content_type, md5: md5)
file.close
puts key
end
end
end

Related

File not created when running a task locally

I have a task that is to output a CSV. I can confirm that the CSV is in memory but when I call the following, the file is not created
File.open(File.join(Rails.root, 'tmp', file_name), 'w') do |file|
file.write(csv_export)
end
I think it might be a permissions issue because the task is running in the application context but for the life of me can not discover what the user name is to add to tmp folder permission
you could check the current permission of that tmp folder (readable?, writable?, executable?), you could also check whether the permission requester is the folder owner or group owner with (owned?, grpowned?), then you could grant the permission for tmp folder by chmod to create file_name
tmp_folder = File.join(Rails.root, 'tmp')
if File.owned?(tmp_folder) && !File.executable?(tmp_folder)
File.chmod(0700, tmp_folder)
end
File.open(File.join(tmp_folder,file_name), 'w') do |file|
file.write(csv_export)
end

Getting "Errno::ENOENT: No such file or directory # rb_sysopen" while reading the CSV file from the AWS S3

I have application which is deployed to Heroku. I have added functionality for uploading users thorough the CSV. For this I have provided CSV upload functionality (Used Paperclip gem).
Here is my code for reading the file and creating new user
def import(file)
CSV.foreach(file.path, headers: true) do |row|
row_hash = row.to_hash.values
data = row_hash[0].split("\t")
.
.
.
end
On the local it is working fine. But on the heroku it is giving me following error
Errno::ENOENT: No such file or directory # rb_sysopen - https://s3.amazonaws.com/..../..../sample_csv(2).csv
I referred following links
Errno::ENOENT (No such file or directory) in amazon-s3
File reading from Amazon server, ruby on rails, no match route
but didn't any success. For more debugging, I tried same url from my local rails console and it is giving me the same error.
2.2.2 :008 > cp = "https://s3.amazonaws.com/..../..../sample_csv(2).csv"
2.2.2 :008 > f = File.open(cp, "r")
Errno::ENOENT: No such file or directory # rb_sysopen - https://s3.amazonaws.com
Also tried open uri http://ruby-doc.org/stdlib-2.1.0/libdoc/open-uri/rdoc/OpenURI.html.
I can download the same file from the browser.
Can any one let me know how to resolve this error. Is there any bucket permission issue (I have already provided open access for the bucket).
Try this
require 'open-uri'
require 'csv'
def import(file)
CSV.new(open(file), :headers => :true).each do |row| #First open the file using open
row_hash = row.to_hash.values
data = row_hash[0].split("\t")
.
.
.
end
For more info you can refer this link

Sitemap_generator fails to upload

I've followed the instructions on a couple of pages for getting a sitemap to generate and be uploaded to my S3 Bucket. The sitemap is generating, but not uploading.
I'm using carrierwave for the upload, which is working fine for image uploads.
The key file seems to be config/sitemap.rb. Here's mine:
require 'rubygems'
require 'sitemap_generator'
# Set the host name for URL creation
SitemapGenerator::Sitemap.default_host = "https://www.driverhunt.com"
# pick a place safe to write the files
SitemapGenerator::Sitemap.public_path = 'tmp/'
# store on S3 using #Fog# Carrierwave
SitemapGenerator::Sitemap.adapter = SitemapGenerator::WaveAdapter.new
# SitemapGenerator::Sitemap.adapter = SitemapGenerator::S3Adapter.new
# This is a different problem to the one in the question, but using this second adaptor gives the error: "...lib/fog/storage.rb:27:in `new': is not a recognized storage provider (ArgumentError)"
# inform the map cross-linking where to find the other maps
SitemapGenerator::Sitemap.sitemaps_host = "http://#{ENV['S3_BUCKET']}.s3.amazonaws.com/"
# pick a namespace within your bucket to organize your maps
SitemapGenerator::Sitemap.sitemaps_path = 'sitemaps/'
SitemapGenerator::Sitemap.create do
add '/home', :changefreq => 'daily', :priority => 0.9
# add '/contact_us', :changefreq => 'weekly'
end
# SitemapGenerator::Sitemap.ping_search_engines # Not needed if you use the rake tasks
What's going on? How do I debug a carrierwave upload?
I will answer the question as your comment for the S3Adapter brought me to this topic while I was googling the not recognized provider. If you turn back on the comment using the S3Adapter and do the following you will get it working.
If you do not specify any fog ENV VARS for the fog-aws gem you will get the error:
ArgumentError: is not a recognized provider
by using as an adapter the SitemapGenerator::S3Adapter.new
The setup you have got above is perfectly fine, just use the S3Adapter.new instead of the WaveAdapter!
The error you are getting (and I was getting as well) is due to the fact that SitemapGenerator::S3Adapter uses fog-aws and in order to make it run by default you should have the following ENV VARS:
ENV['AWS_ACCESS_KEY_ID'] = XXX
ENV['AWS_SECRET_ACCESS_KEY'] = XXX
ENV['FOG_PROVIDER'] = AWS
ENV['FOG_DIRECTORY'] = your-bucket-name
ENV['FOG_REGION'] = your-bucket-region (ex: us-west-2)
If you are missing even one of the following you will get the error:
ArgumentError: is not a recognized provider
Alternativelly, if you want to avoid using ENV VARS for some reason you should specify the values when you initialize your adapter as follows:
SitemapGenerator::Sitemap.adapter = SitemapGenerator::S3Adapter.new(fog_provider: 'AWS',
aws_access_key_id: 'your-access-key-id',
aws_secret_access_key: 'your-access-key',
fog_directory: 'your-bucket',
fog_region: 'your-aws-region')
However using just the above ENV VARS you will be fine and get your sitemap up and running. This setup was tested with sitemap_generator version: 5.1.0
For your question:
The Image uploading works as it does not require the exact same configuration as the WaveAdapter. I am guessing that your carrierwave.rb file is missing the following:
config.cache_dir = "#{Rails.root}/tmp/"
config.permissions = 0666
The complete configuration for the carrierwave initializer can be found here:
Generate Sitemaps on read only filesystems like Heroku (check if you are missing something or use the other adapter)
However, I believe that your problem has to do with missing ENV VARS from the production environment.

Rails 4, Paperclip, Upload in FTP. How to rename?

I'm using Rails 4 and Paperclip.
Beacuse I need to upload files on FTP server i'm using this great gem:
https://github.com/xing/paperclip-storage-ftp
Everything works perfect in local, but in FTP I can't rename files using this code:
def rename_myfile
if self.rename.present?
path = self.myfile.path
FileUtils.move(myfile.path, File.join(File.dirname(myfile.path), self.rename))
self.myfile_file_name = self.rename
end
end
I got an error:
No such file or directory # sys_fail2 - (/myfiles/19/original/myfileOriginalName.jpg, /myfiles/19/original/myfileRenamedName.jpg)
How to enter in ftp with FileUtils.move???
Create and Delete are working very well!
https://github.com/xing/paperclip-storage-ftp/issues/28
You have to build the full path to the file not just the file's dirname and name. change your FileUtils.move line to this:
orig_full_path = Rails.root.join "public", myfile.path # assuming they're saved in your public directory
new_full_path = Rails.root.join "public", File.dirname(myfile.path), self.rename
FileUtils.move orig_full_path, new_full_path
The idea here is to get the absolute path to your files. Before you were just giving FileUtils this path: /myfiles/19/original/myfileOriginalName.jpg which means it will look for the file in a folder /myfiles in the root of your file system. But they're actually in your Rails folder. So you should use Rails.root.join to get the true absolute path: /Users/me/my_rails_project/public/myfiles/19/original/myfileOriginalName.jpg.

Rails Backup: Generating Backup Model not running with Dokku

Using Backup gem to do a DB backup, deployed the app with the gem installed to DigitalOcean and the next step is to run the generator using
dokku run oktob bundle exec backup generate:model --trigger oktob_db_backup --databases="postgresql" --storages="dropbox" --encryptors="openssl" --compressors="gzip" --notifiers="mail"
This should create the configuration files to setup the backup, but it returns nothing.
When I run the generator on my local machine, 2 files are generated as normal, but this time without using dokku run oktob as it's on a local machine.
Generated model file: '/Users/ahmadajmi/Backup/models/oktob_db_backup.rb'.
Generated configuration file: '/Users/ahmadajmi/Backup/config.rb'.
Thanks
To solve the problem, the backup gem should be installed outside the app container in the Linux machine.
Backup generator command is used to to generate the backup config file.
backup generate:model --trigger oktob_database_backup --databases="postgresql" --storages="s3" --compressor="gzip" --notifiers="mail"
Which will create the backup configuration file /root/Backup/models/oktob_database_backup.rb in the Linux machine and not related to Dokku, or app container, and this file contains some required configuration related to the database connection, S3 information, and email account to send notifications.
/root/Backup/models/oktob_database_backup.rb file contains:
# encoding: utf-8
##
# Backup Generated: oktob_db_backup
# Once configured, you can run the backup with the following command:
#
# $ backup perform -t oktob_db_backup [-c <path_to_configuration_file>]
#
# For more information about Backup's components, see the documentation at:
# http://backup.github.io/backup
#
Model.new(:oktob_database_backup, 'Oktob Production Database Backup') do
##
# PostgreSQL [Database]
#
database PostgreSQL do |db|
db.name = ENV['DATABASE_NAME']
db.username = ENV['DATABASE_USERNAME']
db.password = ENV['DATABASE_PASSWORD']
db.host = ENV['DATABASE_HOST']
db.port = ENV['DATABASE_PORT']
end
##
# Amazon Simple Storage Service [Storage]
#
store_with S3 do |s3|
# AWS Credentials
s3.access_key_id = ENV['AWS_ACCESS_KEY_ID']
s3.secret_access_key = ENV['AWS_SECRET_ACCESS_KEY']
s3.region = "us-west-2"
s3.bucket = ENV['AWS_DATABASE_BACKUP_BUCKET_NAME']
s3.path = "/"
end
##
# Gzip [Compressor]
#
compress_with Gzip
##
# Mail [Notifier]
#
# The default delivery method for Mail Notifiers is 'SMTP'.
# See the documentation for other delivery options.
#
notify_by Mail do |mail|
mail.on_success = true
mail.on_warning = true
mail.on_failure = true
mail.from = ENV['EMAIL_ADDRESS']
mail.to = ENV['EMAIL_ADDRESS']
mail.address = "smtp.gmail.com"
mail.port = 587
mail.domain = "oktob.io"
mail.user_name = ENV['SMTP_USERNAME']
mail.password = ENV['SMTP_PASSWORD']
mail.authentication = "plain"
mail.encryption = :starttls
end
end
The configurations are stored in /etc/environment file as:
## Database
export DATABASE_NAME=''
export DATABASE_USERNAME=''
export DATABASE_PASSWORD=''
export DATABASE_HOST=''
export DATABASE_PORT=''
## Amazon S3
export AWS_ACCESS_KEY_ID=''
export AWS_SECRET_ACCESS_KEY=''
export AWS_DATABASE_BACKUP_BUCKET_NAME=''
## Email
export EMAIL_ADDRESS=''
export SMTP_USERNAME=''
export SMTP_PASSWORD=''
To perform a backup manually we can do type backup perform -t oktob_database_backup, and this command will do 3 things:
Connect remotely to the Postgres Dokku container and make a database dump
Storing the abckup file into oktob-database-backup S3 bucket.
Send an email notification to EMAIL_ADDRESS
The backup is done every one hour using a cron job, this job added in crontab -e as:
0 * * * * /bin/bash -l -c '/usr/local/rvm/gems/ruby-2.0.0-p647/bin/backup perform -t oktob_database_backup'
We can get the /usr/local/rvm/gems/ruby-2.0.0-p647/bin/backup part by typing which backup in the command line, to get the path.
Good articles to check:
Hourly Production Server Database And File Backups
Backup PostgreSQL from a Rails project to Amazon S3
This is a no-answer, but too long for a comment.
You will generally have the problem, that the newly created files are not persisted, because the dokku run stuff fires up a new container (which then directly reaches its end of life).
You can use the dokku-volume plugin to designate a directory that "lives" outside of your app-containers and keeps the files as-is.

Resources