Skip to content

Commit

Permalink
Add Backblaze B2 Cloud Storage
Browse files Browse the repository at this point in the history
  • Loading branch information
duartemvix committed Dec 11, 2020
1 parent 15dc1ef commit e4909b9
Show file tree
Hide file tree
Showing 12 changed files with 321 additions and 9 deletions.
21 changes: 15 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,13 @@ Or, to use Azure Blob storage, configure as this.
config.action_controller.asset_host = "//#{ENV['AZURE_STORAGE_ACCOUNT_NAME']}.blob.core.windows.net/#{ENV['FOG_DIRECTORY']}"
```

Or, to use Backblaze B2, configure as this.

``` ruby
# The path for this host should equal to /file/<your_public_bucket>/
config.action_controller.asset_host = "//f000.backblazeb2.com/file/#{ENV['FOG_DIRECTORY']}"
```


On **HTTPS**: the exclusion of any protocol in the asset host declaration above will allow browsers to choose the transport mechanism on the fly. So if your application is available under both HTTP and HTTPS the assets will be served to match.

Expand Down Expand Up @@ -170,7 +177,7 @@ heroku config:add FOG_DIRECTORY=xxxx
heroku config:add FOG_PROVIDER=Rackspace
```

Google Storage Cloud configuration is supported as well. The preferred option is using the [GCS JSON API](https://github.com/fog/fog-google#storage) which requires that you create an appropriate service account, generate the signatures and make them accessible to asset sync at the prescribed location
Google Storage Cloud configuration is supported as well. The preferred option is using the [GCS JSON API](https://github.com/fog/fog-google#storage) which requires that you create an appropriate service account, generate the signatures and make them accessible to asset sync at the prescribed location

```bash
heroku config:add FOG_PROVIDER=Google
Expand Down Expand Up @@ -199,6 +206,7 @@ Run the included Rake task to generate a starting point.
rails g asset_sync:install --provider=Rackspace
rails g asset_sync:install --provider=AWS
rails g asset_sync:install --provider=AzureRM
rails g asset_sync:install --provider=Backblaze

The generator will create a Rails initializer at `config/initializers/asset_sync.rb`.

Expand Down Expand Up @@ -245,7 +253,7 @@ AssetSync.configure do |config|
#
# Upload files concurrently
# config.concurrent_uploads = false
#
#
# Number of threads when concurrent_uploads is enabled
# config.concurrent_uploads_max_threads = 10
#
Expand Down Expand Up @@ -274,6 +282,7 @@ Run the included Rake task to generate a starting point.
rails g asset_sync:install --use-yml --provider=Rackspace
rails g asset_sync:install --use-yml --provider=AWS
rails g asset_sync:install --use-yml --provider=AzureRM
rails g asset_sync:install --use-yml --provider=Backblaze

The generator will create a YAML file at `config/asset_sync.yml`.

Expand Down Expand Up @@ -383,7 +392,7 @@ To customize the overrides:
AssetSync.configure do |config|
# Clear the default overrides
config.file_ext_to_mime_type_overrides.clear

# Add/Edit overrides
# Will call `#to_s` for inputs
config.file_ext_to_mime_type_overrides.add(:js, :"application/x-javascript")
Expand All @@ -392,7 +401,7 @@ end
The blocks are run when local files are being scanned and uploaded

#### Fog (Required)
* **fog\_provider**: your storage provider *AWS* (S3) or *Rackspace* (Cloud Files) or *Google* (Google Storage) or *AzureRM* (Azure Blob)
* **fog\_provider**: your storage provider *AWS* (S3) or *Rackspace* (Cloud Files) or *Google* (Google Storage) or *AzureRM* (Azure Blob) or *Backblaze* (Backblaze B2)
* **fog\_directory**: your bucket name

#### Fog (Optional)
Expand Down Expand Up @@ -551,7 +560,7 @@ AssetSync.configure do |config|
config.prefix = 'assets'
# Can be a `Pathname` or `String`
# Will be converted into an `Pathname`
# If relative, will be converted into an absolute path
# If relative, will be converted into an absolute path
# via `::Rails.root` or `::Dir.pwd`
config.public_path = Pathname('./public')
end
Expand Down Expand Up @@ -622,7 +631,7 @@ Make sure you have a .env file with these details:-
AWS_SECRET_ACCESS_KEY=<yoursecretkey>
FOG_DIRECTORY=<yourbucket>
FOG_REGION=<youbucketregion>

# for AzureRM provider
AZURE_STORAGE_ACCOUNT_NAME=<youraccountname>
AZURE_STORAGE_ACCESS_KEY=<youraccesskey>
Expand Down
1 change: 1 addition & 0 deletions asset_sync.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Gem::Specification.new do |s|

s.add_development_dependency "fog-aws"
s.add_development_dependency "fog-azure-rm"
s.add_development_dependency "fog-backblaze"

s.add_development_dependency "uglifier"
s.add_development_dependency "appraisal"
Expand Down
20 changes: 20 additions & 0 deletions lib/asset_sync/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ class Invalid < StandardError; end
attr_accessor :azure_storage_account_name
attr_accessor :azure_storage_access_key

# Backblaze B2 with Fog::Backblaze
attr_accessor :b2_key_id
attr_accessor :b2_key_token
attr_accessor :b2_bucket_id

validates :existing_remote_files, :inclusion => { :in => %w(keep delete ignore) }

validates :fog_provider, :presence => true
Expand Down Expand Up @@ -153,6 +158,10 @@ def azure_rm?
fog_provider =~ /azurerm/i
end

def backblaze?
fog_provider =~ /backblaze/i
end

def cache_asset_regexp=(cache_asset_regexp)
self.cache_asset_regexps = [cache_asset_regexp]
end
Expand Down Expand Up @@ -233,6 +242,10 @@ def load_yml!
self.azure_storage_account_name = yml['azure_storage_account_name'] if yml.has_key?("azure_storage_account_name")
self.azure_storage_access_key = yml['azure_storage_access_key'] if yml.has_key?("azure_storage_access_key")

self.b2_key_id = yml['b2_key_id'] if yml.has_key?("b2_key_id")
self.b2_key_token = yml['b2_key_token'] if yml.has_key?("b2_key_token")
self.b2_bucket_id = yml['b2_bucket_id'] if yml.has_key?("b2_bucket_id")

# TODO deprecate the other old style config settings. FML.
self.aws_access_key_id = yml["aws_access_key"] if yml.has_key?("aws_access_key")
self.aws_secret_access_key = yml["aws_access_secret"] if yml.has_key?("aws_access_secret")
Expand Down Expand Up @@ -293,6 +306,13 @@ def fog_options
:azure_storage_access_key => azure_storage_access_key,
})
options.merge!({:environment => fog_region}) if fog_region
elsif backblaze?
require 'fog/backblaze'
options.merge!({
:b2_key_id => b2_key_id,
:b2_key_token => b2_key_token,
:b2_bucket_id => b2_bucket_id,
})
else
raise ArgumentError, "AssetSync Unknown provider: #{fog_provider} only AWS, Rackspace and Google are supported currently."
end
Expand Down
4 changes: 4 additions & 0 deletions lib/asset_sync/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ class Engine < Rails::Engine
config.azure_storage_account_name = ENV['AZURE_STORAGE_ACCOUNT_NAME'] if ENV.has_key?('AZURE_STORAGE_ACCOUNT_NAME')
config.azure_storage_access_key = ENV['AZURE_STORAGE_ACCESS_KEY'] if ENV.has_key?('AZURE_STORAGE_ACCESS_KEY')

config.b2_key_id = ENV['B2_KEY_ID'] if ENV.has_key?('B2_KEY_ID')
config.b2_key_token = ENV['B2_KEY_TOKEN'] if ENV.has_key?('B2_KEY_TOKEN')
config.b2_bucket_id = ENV['B2_BUCKET_ID'] if ENV.has_key?('B2_BUCKET_ID')

config.enabled = (ENV['ASSET_SYNC_ENABLED'] == 'true') if ENV.has_key?('ASSET_SYNC_ENABLED')

config.existing_remote_files = ENV['ASSET_SYNC_EXISTING_REMOTE_FILES'] || "keep"
Expand Down
9 changes: 8 additions & 1 deletion lib/asset_sync/storage.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,14 @@ def connection

def bucket
# fixes: https://github.com/rumblelabs/asset_sync/issues/18
@bucket ||= connection.directories.get(self.config.fog_directory, :prefix => self.config.assets_prefix)

# Backblaze B2 bucket objects do not need neither a path nor a prefix in order to be instantiated. However, as far as I've seen setting a prefix on this call could be redundant and the keyword argument could be removed
@bucket ||= if self.config.backblaze?
connection.directories.get(self.config.fog_directory)
else
connection.directories.get(self.config.fog_directory, :prefix => self.config.assets_prefix)
end

end

def log(msg)
Expand Down
18 changes: 17 additions & 1 deletion lib/generators/asset_sync/install_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class InstallGenerator < Rails::Generators::Base

# Commandline options can be defined here using Thor-like options:
class_option :use_yml, :type => :boolean, :default => false, :desc => "Use YML file instead of Rails Initializer"
class_option :provider, :type => :string, :default => "AWS", :desc => "Generate with support for 'AWS', 'Rackspace', 'Google', or 'AzureRM"
class_option :provider, :type => :string, :default => "AWS", :desc => "Generate with support for 'AWS', 'Rackspace', 'Google', 'AzureRM', or 'Backblaze'"

def self.source_root
@source_root ||= File.join(File.dirname(__FILE__), 'templates')
Expand All @@ -27,6 +27,10 @@ def azure_rm?
options[:provider] == 'AzureRM'
end

def backblaze?
options[:provider] == 'Backblaze'
end

def aws_access_key_id
"<%= ENV['AWS_ACCESS_KEY_ID'] %>"
end
Expand Down Expand Up @@ -63,6 +67,18 @@ def azure_storage_access_key
"<%= ENV['AZURE_STORAGE_ACCESS_KEY'] %>"
end

def b2_key_id
"<%= ENV['B2_KEY_ID'] %>"
end

def b2_key_token
"<%= ENV['B2_KEY_TOKEN'] %>"
end

def b2_bucket_id
"<%= ENV['B2_BUCKET_ID'] %>"
end

def app_name
@app_name ||= Rails.application.is_a?(Rails::Application) && Rails.application.class.name.sub(/::Application$/, "").downcase
end
Expand Down
6 changes: 6 additions & 0 deletions lib/generators/asset_sync/templates/asset_sync.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@
config.azure_storage_account_name = ENV['AZURE_STORAGE_ACCOUNT_NAME']
config.azure_storage_access_key = ENV['AZURE_STORAGE_ACCESS_KEY']

<%- elsif backblaze? -%>
config.fog_provider = 'Backblaze'
config.b2_key_id = ENV['B2_KEY_ID']
config.b2_key_token = ENV['B2_KEY_TOKEN']
config.b2_bucket_id = ENV['B2_BUCKET_ID']
# config.fog_directory specifies container name of Azure Blob storage
<%- end -%>
config.fog_directory = ENV['FOG_DIRECTORY']
Expand Down
5 changes: 5 additions & 0 deletions lib/generators/asset_sync/templates/asset_sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ defaults: &defaults
fog_provider: 'AzureRM'
azure_storage_account_name: "<%= azure_storage_account_name %>"
azure_storage_access_key: "<%= azure_storage_access_key %>"
<%- elsif backblaze? -%>
fog_provider: 'Backblaze'
b2_key_id: "<%= b2_key_id %>"
b2_key_token: "<%= b2_key_token %>"
b2_bucket_id: "<%= b2_bucket_id %>"

# fog_directory specifies container name of Azure Blob storage
<%- end -%>
Expand Down
20 changes: 20 additions & 0 deletions spec/fixtures/backblaze_with_yml/config/asset_sync.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
defaults: &defaults
fog_provider: "Backblaze"
b2_key_id: 'xxxx'
b2_key_token: 'zzzz'
b2_bucket_id: '1234'

development:
<<: *defaults
fog_directory: "rails_app_development"
existing_remote_files: keep

test:
<<: *defaults
fog_directory: "rails_app_test"
existing_remote_files: keep

production:
<<: *defaults
fog_directory: "rails_app_production"
existing_remote_files: delete
1 change: 0 additions & 1 deletion spec/integration/azure_rm_integration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,3 @@ def execute(command)
end

end

74 changes: 74 additions & 0 deletions spec/integration/backblaze_intergration_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
require File.dirname(__FILE__) + '/../spec_helper'
require "fog/backblaze"

def bucket(name)
options = {
:provider => 'Backblaze',
:b2_key_id => ENV['B2_KEY_ID'],
:b2_key_token => ENV['B2_KEY_TOKEN'],
:b2_bucket_id => ENV['B2_BUCKET_ID']
}
options.merge!({ :environment => ENV['FOG_REGION'] }) if ENV.has_key?('FOG_REGION')

connection = Fog::Storage.new(options)
connection.directories.get(ENV['FOG_DIRECTORY'])
end

def execute(command)
app_path = File.expand_path("../../dummy_app", __FILE__)
Dir.chdir app_path
`#{command}`
end

describe "AssetSync" do

before(:each) do
@prefix = SecureRandom.hex(6)
end

let(:app_js_regex){
/#{@prefix}\/application-[a-zA-Z0-9]*.js$/
}

let(:app_js_gz_regex){
/#{@prefix}\/application-[a-zA-Z0-9]*.js.gz$/
}

let(:files){ bucket(@prefix).files }


after(:each) do
@directory = bucket(@prefix)
@directory.files.each do |f|
f.destroy
end
end

it "sync" do
execute "rake ASSET_SYNC_PREFIX=#{@prefix} assets:precompile"

files = bucket(@prefix).files

app_js = files.select{ |f| f.key =~ app_js_regex }.first
expect(app_js.content_type).to eq("application/javascript")

app_js_gz = files.select{ |f| f.key =~ app_js_gz_regex }.first
expect(app_js_gz.content_type).to eq("application/javascript")
expect(app_js_gz.content_encoding).to eq("gzip")
end

it "sync with enabled=false" do
execute "rake ASSET_SYNC_PREFIX=#{@prefix} ASSET_SYNC_ENABLED=false assets:precompile"
expect(bucket(@prefix).files.size).to eq(0)
end

it "sync with gzip_compression=true" do
execute "rake ASSET_SYNC_PREFIX=#{@prefix} ASSET_SYNC_GZIP_COMPRESSION=true assets:precompile"
# bucket(@prefix).files.size.should == 3

app_js_path = files.select{ |f| f.key =~ app_js_regex }.first
app_js = files.get( app_js_path.key )
expect(app_js.content_type).to eq("application/javascript")
end

end
Loading

0 comments on commit e4909b9

Please sign in to comment.