Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow skipping tenants when running patches #36

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,24 @@ end
*Note:* Make sure your sidekiq queue is able to process concurrent jobs.
You can use ```config.sidekiq_options``` to customise it.

### Skipping tenants when running patches for multiple tenants

If you are using the Apartment gem, the patches will run across all tenants by default. If you wish to only run patches against a subset of tenants, you can use the `ONLY_TENANTS` env var, like so

```bash
# This will only run for my_tenant and other_tenant, provided they are listed as tenants by the Apartment gem
ONLY_TENANTS=my_tenant,other_tenant bundle exec rake patches:run
```

Similarly if you want to run patches against all tenants _except_ for a select few, you can use the `SKIP_TENANTS` env var, like so

```bash
# This will run for all tenants EXCEPT my_tenant and other_tenant
SKIP_TENANTS=my_tenant,other_tenant bundle exec rake patches:run
```

If you specify both env vars, the `ONLY_TENANTS` env var will take precedence

### Application version verification

In environments where a rolling update of sidekiq workers is performed during the deployment, multiple versions of the application run at the same time. If a Patches job is scheduled by the new application version during the rolling update, there is a possibility that it can be executed by the old application version, which will not have all the required patch files.
Expand Down
1 change: 1 addition & 0 deletions lib/patches.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ def self.logger=(log)
require "patches/tenant_runner"
require "patches/notifier"
require "patches/worker" if defined?(Sidekiq)
require "patches/tenant_finder"
28 changes: 28 additions & 0 deletions lib/patches/tenant_finder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
class Patches::TenantFinder

def tenant_names
@tenant_names ||= begin
if only_tenant_names.any?
apartment_tenant_names.select { |tenant_name| only_tenant_names.include?(tenant_name) }
elsif skip_tenant_names.any?
apartment_tenant_names.reject { |tenant_name| skip_tenant_names.include?(tenant_name) }
else
apartment_tenant_names
end
end
end

private

def apartment_tenant_names
Apartment.tenant_names || []
end

def only_tenant_names
ENV['ONLY_TENANTS'] ? ENV['ONLY_TENANTS'].split(',').map { |s| s.strip } : []
end

def skip_tenant_names
ENV['SKIP_TENANTS'] ? ENV['SKIP_TENANTS'].split(',').map { |s| s.strip } : []
end
end
5 changes: 2 additions & 3 deletions lib/patches/tenant_runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ class Patches::TenantRunner
include Patches::TenantRunConcern
attr_accessor :path

def initialize(path: nil, tenants: nil)
def initialize(path: nil)
@path = path
@tenants = tenants
end

def perform
Expand All @@ -23,7 +22,7 @@ def perform
end

def tenants
@tenants ||= (Apartment.tenant_names || [])
@tenants ||= Patches::TenantFinder.new.tenant_names
end

private
Expand Down
4 changes: 0 additions & 4 deletions lib/tasks/patches.rake
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ namespace :patches do
end
end

def tenants
ENV['DB'] ? ENV['DB'].split(',').map { |s| s.strip } : Apartment.tenant_names || []
end

task :pending => [:environment] do
Patches::Pending.new.each do |patch|
puts patch
Expand Down
49 changes: 49 additions & 0 deletions spec/tenant_finder_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
require 'spec_helper'
require 'patches/tenant_finder'

describe Patches::TenantFinder do
describe '#tenant_names' do
subject { tenant_finder.tenant_names }
let(:tenant_finder) { Patches::TenantFinder.new }
let(:apartment_tenant_names) { ['test1', 'test2', 'test3'] }
let(:only_tenants_env_var) { nil }
let(:skip_tenants_env_var) { nil }

before do
allow(Apartment).to receive(:tenant_names).and_return(apartment_tenant_names)
allow(ENV).to receive(:[]).with('ONLY_TENANTS').and_return(only_tenants_env_var)
allow(ENV).to receive(:[]).with('SKIP_TENANTS').and_return(skip_tenants_env_var)
end

context 'when no env vars are set' do
it 'returns the list from Apartment' do
expect(subject).to match_array(['test1', 'test2', 'test3'])
end
end

context 'when ONLY_TENANTS is set' do
let(:only_tenants_env_var) { 'test1,test2' }

it 'returns a subset of the Apartment list filtered by the env var' do
expect(subject).to match_array(['test1', 'test2'])
end
end

context 'when SKIP_TENANTS is set' do
let(:skip_tenants_env_var) { 'test1,test2' }

it 'returns a subset of the Apartment list excluding names set by the env var' do
expect(subject).to match_array(['test3'])
end
end

context 'when ONLY_TENANTS and SKIP_TENANTS are set' do
let(:only_tenants_env_var) { 'test1' }
let(:skip_tenants_env_var) { 'test1' }

it 'prioritises the ONLY_TENANTS env var' do
expect(subject).to match_array(['test1'])
end
end
end
end
9 changes: 2 additions & 7 deletions spec/tenant_runner_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,11 @@ module Tenant
allow(Patches::Config.configuration).to receive(:application_version) { application_version }
end

context 'with tenants' do
let(:tenants) { ['tenants'] }
subject { described_class.new(tenants: tenants) }
specify { expect(subject.tenants).to eql(tenants) }
end

context 'perform' do
let(:tenant_names) { ['test'] }
let(:tenant_finder) { double(Patches::TenantFinder, tenant_names: tenant_names) }

before { expect(Apartment).to receive(:tenant_names).and_return(tenant_names) }
before { allow(Patches::TenantFinder).to receive(:new).and_return(tenant_finder) }

specify do
expect(subject.tenants).to eql(['test'])
Expand Down