Skip to content

Commit

Permalink
Merge pull request #5 from gleopoldo/change-dump-host-and-user
Browse files Browse the repository at this point in the history
Change dump host and user
  • Loading branch information
rhruiz authored Feb 15, 2019
2 parents 32ae9a7 + ce5cb72 commit 9068262
Show file tree
Hide file tree
Showing 18 changed files with 598 additions and 84 deletions.
8 changes: 8 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
FROM ruby:2.0

ARG uid

RUN apt-get update && apt-get install -y postgresql-client

RUN useradd -M -u $uid mtools
USER mtools
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ dumper = MultitenancyTools::SchemaDumper.new('database name', 'schema name')
dumper.dump_to('path/to/file.sql')
```

#### Dumping from a different host and using a different username
```ruby
options = { host: 'db-on-docker', username: 'non-root-user' }
dumper = MultitenancyTools::SchemaDumper.new('database name', 'schema name', options)
dupmer.dump_to('path/to/file.sql')
```

### Dumping the content of a table to a SQL file

Like `SchemaDumper`, this tool also requires `pg_dump` to be on the `PATH`:
Expand All @@ -49,6 +56,13 @@ dumper = MultitenancyTools::TableDumper.new('database name', 'schema name', 'tab
dumper.dump_to('path/to/file.sql')
```

#### Dumping from a different host and using a different username
```ruby
options = { host: 'db-on-docker', username: 'non-root-user' }
dumper = MultitenancyTools::TableDumper.new('database name', 'schema name', 'table_name', options)
dupmer.dump_to('path/to/file.sql')
```

### Creating a new PostgreSQL schema using a SQL file as template

After using `SchemaDumper` to create the SQL template, you can use the following
Expand All @@ -72,6 +86,15 @@ this database *will be destroyed and recreated* on test execution.
You can use `bin/console` to get an interactive prompt that will allow you to
experiment.

You can also run this project using docker in your local environment. Just
ensure that you have:

* Docker equal or greater than 18.09.1

Then, build *Multitenancy Tools* image running `scripts/setup`

In order to access it's console, run `scripts/bash`

## Releasing a new version

If you are the maintainer of this project:
Expand Down
2 changes: 2 additions & 0 deletions lib/multitenancy_tools.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
require 'multitenancy_tools/version'
require 'multitenancy_tools/errors'
require 'multitenancy_tools/dump_cleaner'
require 'multitenancy_tools/dump/data_only'
require 'multitenancy_tools/dump/schema_only'
require 'multitenancy_tools/schema_dumper'
require 'multitenancy_tools/schema_creator'
require 'multitenancy_tools/table_dumper'
Expand Down
38 changes: 38 additions & 0 deletions lib/multitenancy_tools/dump/data_only.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
require 'open3'

module MultitenancyTools
module Dump
class DataOnly
def initialize(options)
@schema = options.fetch(:schema)
@db = options.fetch(:database)
@host = options.fetch(:host, nil)
@user = options.fetch(:username, nil)
@table = options.fetch(:table)
end

def dump
Open3.capture3(dump_args.shelljoin)
end

private

def dump_args
args = [
'pg_dump',
'--table', "#{@schema}.#{@table}",
'--no-privileges',
'--no-tablespaces',
'--no-owner',
'--dbname', @db,
'--data-only',
'--inserts'
]

args << ['--host', @host] if @host.present?
args << ['--username', @user] if @user.present?
args.flatten
end
end
end
end
36 changes: 36 additions & 0 deletions lib/multitenancy_tools/dump/schema_only.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
require 'open3'

module MultitenancyTools
module Dump
class SchemaOnly
def initialize(options)
@schema = options.fetch(:schema)
@database = options.fetch(:database)
@host = options.fetch(:host, nil)
@user = options.fetch(:username, nil)
end

def dump
Open3.capture3(dump_args.shelljoin)
end

private

def dump_args
args = [
'pg_dump',
'--schema', @schema,
'--schema-only',
'--no-privileges',
'--no-tablespaces',
'--no-owner',
'--dbname', @database,
]

args << ['--host', @host] if @host.present?
args << ['--username', @user] if @user.present?
args.flatten
end
end
end
end
25 changes: 24 additions & 1 deletion lib/multitenancy_tools/dump_cleaner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ module MultitenancyTools
# be present on dumps generated by {SchemaDumper} and {TableDumper}.
class DumpCleaner
# @param [String] sql
def initialize(sql)
def initialize(sql, schema_name = '')
@schema_name = schema_name
@sql = sql.dup
end

Expand All @@ -17,7 +18,29 @@ def clean
@sql.gsub!(/SET lock_timeout .*;\n/, '')
@sql.gsub!(/^--(?:.*)\n+/, '')
@sql.gsub!(/\n+/, "\n")
clean_schema_names!
clean_catalog_overwrites!
@sql
end

private

# Matches namespaces containing the given schema_name
#
# For instance:
# CREATE TABLE schema1.posts ();
#
# Will be replaced by:
# CREATE TABLE posts ();
def clean_schema_names!
return if @schema_name.blank?

@sql.gsub!(/\b#{@schema_name}\.([\S\D]*)/, '\1')
end

# Removes system administrators configs overwrites
def clean_catalog_overwrites!
@sql.gsub!(/SELECT pg_catalog\.set_config.*;\n/, '')
end
end
end
21 changes: 10 additions & 11 deletions lib/multitenancy_tools/schema_dumper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ module MultitenancyTools
class SchemaDumper
# @param database [String] database name
# @param schema [String] schema name
def initialize(database, schema)
def initialize(database, schema, options = {})
@database = database
@schema = schema
@host = options.fetch(:host, '')
@username = options.fetch(:username, '')
end

# Generates a dump an writes it into a file. Please see {IO.new} for open
Expand All @@ -34,20 +36,17 @@ def initialize(database, schema)
# @param file [String] file path
# @param mode [String] IO open mode
def dump_to(file, mode: 'w')
stdout, stderr, status = Open3.capture3(
'pg_dump',
'--schema', @schema,
'--schema-only',
'--no-privileges',
'--no-tablespaces',
'--no-owner',
'--dbname', @database
)
stdout, stderr, status = Dump::SchemaOnly.new(
schema: @schema,
host: @host,
username: @username,
database: @database
).dump

fail(PgDumpError, stderr) unless status.success?

File.open(file, mode) do |f|
f.write DumpCleaner.new(stdout).clean
f.write DumpCleaner.new(stdout, @schema).clean
end
end
end
Expand Down
23 changes: 11 additions & 12 deletions lib/multitenancy_tools/table_dumper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ class TableDumper
# @param database [String] database name
# @param schema [String] schema name
# @param table [String] table name
def initialize(database, schema, table)
def initialize(database, schema, table, options = {})
@database = database
@schema = schema
@table = table
@host = options.fetch(:host, '')
@username = options.fetch(:username, '')
end

# Generates a dump an writes it into a file. Please see {IO.new} for open
Expand All @@ -34,21 +36,18 @@ def initialize(database, schema, table)
# @param file [String] file path
# @param mode [String] IO open mode
def dump_to(file, mode: 'w')
stdout, stderr, status = Open3.capture3(
'pg_dump',
'--table', "#{@schema}.#{@table}",
'--data-only',
'--no-privileges',
'--no-tablespaces',
'--no-owner',
'--inserts',
'--dbname', @database
)
stdout, stderr, status = Dump::DataOnly.new(
table: @table,
schema: @schema,
database: @database,
host: @host,
username: @username
).dump

fail(PgDumpError, stderr) unless status.success?

File.open(file, mode) do |f|
f.write DumpCleaner.new(stdout).clean
f.write DumpCleaner.new(stdout, @schema).clean
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion multitenancy_tools.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Gem::Specification.new do |spec|

spec.add_dependency 'activerecord', '~> 4.2.0'
spec.add_dependency 'activesupport', '~> 4.2.0'
spec.add_dependency 'pg'
spec.add_dependency 'pg', '< 1'

spec.add_development_dependency 'bundler', '~> 1.10'
spec.add_development_dependency 'rake', '~> 10.0'
Expand Down
37 changes: 37 additions & 0 deletions scripts/bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/bin/bash

set -e

export POSTGRES_CONTAINER_NAME="postgres"

# removed old pg containers running in your machine
# searches for containers with name "postgres"
remove_old_pg_container() {
container_id=$(docker ps -a --filter name="^$POSTGRES_CONTAINER_NAME$" --format {{.ID}})

if [ "$container_id" ]; then
docker stop $container_id
docker rm $container_id
fi
}

# destroys previous postgres containers and starts a new one
# with fresh data. This prevents problems in running it with
# a prepoulated volume containing old tables.
setup_db_container() {
remove_old_pg_container

docker run --detach \
--publish 5432:5432 \
--hostname $POSTGRES_CONTAINER_NAME \
--name $POSTGRES_CONTAINER_NAME postgres:9.3
}

# ignores docker containers IDs outputs to stdout
setup_db_container &>/dev/null

# brings up main container with multitenancy tools environment
docker run -it \
-v $PWD:/mtools \
--workdir /mtools --user $EUID \
--link $POSTGRES_CONTAINER_NAME mtools bash
5 changes: 5 additions & 0 deletions scripts/setup
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash

set -e

docker build -t mtools:latest --build-arg uid=$EUID .
Loading

0 comments on commit 9068262

Please sign in to comment.