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 templating in config values #32

Merged
merged 6 commits into from
Mar 30, 2021
Merged
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# CHANGELOG

## 0.2.0

- Add templating to configuration values. See [the docs](./docs/config/templating.md) for more info on how to use this feature.

## 0.1.4

- Fix bug caused by missing `require 'open3'` that occurs for some ruby versions
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
ghost_adapter (0.1.4)
ghost_adapter (0.2.0)
activerecord (>= 5)
mysql2 (>= 0.4.0, < 0.6.0)

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Configure your ActiveRecord connection to use `mysql2_ghost` as the adapter in w

For a standard rails project, in `config/database.yml` set `adapter: mysql2_ghost`.

For usage with `DATABASE_URL`, only a _very tiny_ modification is necessary. The URL should be like: `mysql2-ghost://` (notice the `-` instead of `_`). This is because the scheme of a URI must either alphanumeric or one of [`-`, `.`, `+`] ([more details](https://tools.ietf.org/html/rfc3986#section-3.1))
For usage with `DATABASE_URL`, only a _very tiny_ modification is necessary. The URL should be like: `mysql2-ghost://` (notice the `-` instead of `_`). This is because the scheme of a URI must be either alphanumeric or one of [`-`, `.`, `+`] ([more details](https://tools.ietf.org/html/rfc3986#section-3.1))

### Configuration

Expand All @@ -41,7 +41,7 @@ Read more about configuration methods in [the docs](./doc/configuration.md).

### Running Migrations

Since most database activity isn't a migration, we default to identical behavior to the Mysql2Adapter. No need to be executing a bunch of extra logic per query when you're only getting any value for migrations.
Since most database activity isn't a migration, we default to just using the `Mysql2Adapter`. No need to be executing a bunch of extra logic per query when you're only getting any value for migrations.

To enable the ghost adapter, you have two options. First (recommended) is to use the provided rails generator:

Expand Down
6 changes: 3 additions & 3 deletions doc/config/environment_variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ ruby <your command>

## Other Configuration Methods

- [Via Environment Variable](./config/environment_variables.md)
- [Via Rails Configuration Files](./config/rails_configuration_files.md)
- [Via GhostAdapter.setup Method](./config/setup_method.md)
- [Via Environment Variable](./environment_variables.md)
- [Via Rails Configuration Files](./rails_configuration_files.md)
- [Via GhostAdapter.setup Method](./setup_method.md)
6 changes: 3 additions & 3 deletions doc/config/rails_configuration_files.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ end

## Other Configuration Methods

- [Via Environment Variable](./config/environment_variables.md)
- [Via Rails Configuration Files](./config/rails_configuration_files.md)
- [Via GhostAdapter.setup Method](./config/setup_method.md)
- [Via Environment Variable](./environment_variables.md)
- [Via Rails Configuration Files](./rails_configuration_files.md)
- [Via GhostAdapter.setup Method](./setup_method.md)
6 changes: 3 additions & 3 deletions doc/config/setup_method.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,6 @@ end

## Other Configuration Methods

- [Via Environment Variable](./config/environment_variables.md)
- [Via Rails Configuration Files](./config/rails_configuration_files.md)
- [Via GhostAdapter.setup Method](./config/setup_method.md)
- [Via Environment Variable](./environment_variables.md)
- [Via Rails Configuration Files](./rails_configuration_files.md)
- [Via GhostAdapter.setup Method](./setup_method.md)
40 changes: 40 additions & 0 deletions doc/config/templating.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Configuration

## Templating

No matter which configuration method you use, you can use ERB templating to fill in dynamic values. For example, you may want to use the table name, or the timestamp in the path for the throttle flag file. Well, you can do that!

You can use any configuration key as a template variable. Along with the configuration keys, we have added a few additional "magic helpers":

- `pid`: the current process ID
- `timestamp`: current seconds since epoch (as integer)
- `unique_id`: random UUID
- `table`: the table being migrated
- `database`: the database the migration is being run against

### Example

Setting the configuration using some "magic helpers":

```ruby
config.ghost_adapter.throttle_flag_file = '/tmp/<%= table %>/<%= pid %>-<%= timestamp %>.throttle'
```

will result in a value like `/tmp/things/29882-1617119851.throttle`

---

Setting the configuration using your other config:

```ruby
config.ghost_adapter.user = 'great_user'
config.ghost_adapter.panic_flag_file = '/tmp/<%= user %>.panic'
```

will result in a value like `/tmp/great_user.panic`

## Configuration Methods

- [Via Environment Variable](./environment_variables.md)
- [Via Rails Configuration Files](./rails_configuration_files.md)
- [Via GhostAdapter.setup Method](./setup_method.md)
4 changes: 4 additions & 0 deletions doc/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ To get a list of possible configuration options, simply run `gh-ost --help` loca
- [Via Rails Configuration Files](./config/rails_configuration_files.md)
- [Via GhostAdapter.setup Method](./config/setup_method.md)

## Templating

You can use ERB templates to get dynamic values for your configuration. Read more here: [Templating](./config/templating.md)

## Nice to Know

- For boolean args (that do not require a value in the command line), set them as `true`/`false` and they will be either included or excluded accordingly.
Expand Down
16 changes: 15 additions & 1 deletion lib/ghost_adapter/command.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require 'English'

module GhostAdapter
class Command
def initialize(alter:, table:, database: nil, dry_run: false)
Expand All @@ -12,7 +14,7 @@ def to_a
[
EXECUTABLE,
*base_args,
*GhostAdapter.config.as_args,
*config_args,
*execute_arg
]
end
Expand All @@ -37,6 +39,18 @@ def base_args
]
end

def config_args
context = {
pid: $PID,
table: table,
database: database,
timestamp: Time.now.utc.to_i,
unique_id: SecureRandom.uuid
}

GhostAdapter.config.as_args(context: context)
end

def execute_arg
dry_run ? [] : ['--execute']
end
Expand Down
17 changes: 13 additions & 4 deletions lib/ghost_adapter/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,21 +92,30 @@ def compact
to_h.compact
end

def as_args
with_env.map { |key, value| arg(key, value) }.compact
def as_args(context: {})
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

starting to think the whole "args" logic should move from Config and into Command

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that moving at least the ENV vars into the command section makes sense. At the moment understanding the priority order of the different ways to configure options is the most complex part of this library, it would be great to be able to simplify that, out of scope for this PR however.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any chance you wanna make an issue for this and describe the desired outcome? I think you're on to something with ENV config not being merged into config within the config class, but instead the two being merged just-in-time when the command is formed.

full_context = context.merge(compact)
with_env.map { |key, value| arg(key, value, full_context) }.compact
end

private

def arg(key, value)
def arg(key, value, context)
return unless value

hyphenated_key = key.to_s.gsub('_', '-')
if value == true
"--#{hyphenated_key}"
else
"--#{hyphenated_key}=#{value}"
substituted_value = substitute_value(value, context)
"--#{hyphenated_key}=#{substituted_value}"
end
end

def substitute_value(value, context)
return value unless value.is_a? String
return value unless value =~ /<%=.*%>/

ERB.new(value).result_with_hash(context)
end
end
end
2 changes: 1 addition & 1 deletion lib/ghost_adapter/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module GhostAdapter
VERSION = '0.1.4'.freeze
VERSION = '0.2.0'.freeze
end
15 changes: 15 additions & 0 deletions spec/ghost_adapter/command_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,21 @@
expect(command.to_a[0]).to eq('gh-ost')
end

it 'passes pid, timestamp, table, database, unique_id to Config#as_args' do
config = GhostAdapter::Config.new
command = described_class.new(alter: '', table: '', database: '')
expect(GhostAdapter).to receive(:config).and_return(config)
expect(config).to receive(:as_args)
.with(context: {
pid: anything,
timestamp: anything,
table: anything,
database: anything,
unique_id: anything
})
command.to_a
end

describe 'constructor arguments' do
it 'sets the alter argument' do
alter = 'ADD COLUMN foos'
Expand Down
15 changes: 15 additions & 0 deletions spec/ghost_adapter/config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,21 @@
config = described_class.new(options)
expect(config.as_args).to include '--cut-over=default'
end

context 'with ERB templated value' do
it 'substitutes config values' do
options = { user: 'foo', panic_flag_file: '/tmp/<%= user %>.flag' }
config = described_class.new(options)
expect(config.as_args).to include '--panic-flag-file=/tmp/foo.flag'
end

it 'substitutes values passed as context' do
options = { panic_flag_file: '/tmp/<%= foo %>.flag' }
config = described_class.new(options)
args = config.as_args(context: { foo: 'bar' })
expect(args).to include '--panic-flag-file=/tmp/bar.flag'
end
end
end

describe '#merge!' do
Expand Down