fezzik icon indicating copy to clipboard operation
fezzik copied to clipboard

Allow specifying Fezzik env vars outside of destination blocks

Open philc opened this issue 12 years ago • 14 comments

Allow specifying Fezzik env vars outside of destination blocks

A pattern we've commonly seen is to create a big hash of env options, declare it outside of a Fezzik.destination block, and then call Fezzik.env on each of the key value pairs in that hash inside of a Fezzik.destination block:

options = {
  db_host: "localhost",
  unicorn_workers: 1,
  ...
}

Fezzik.destination :vagrant do
  set :domain, "..."
  options.each { |key, value| Fezzik.env key, value }
  ...
end

Fezzik.destination :staging do
  set :domain, "..."
  options.each { |key, value| Fezzik.env key, value }
  ...
end

What we really want to do is just declare these env vars using Fez and let each host override some of them:

Fez.env :db_host, "localhost"
Fez.env :unicorn_workers, 1
...

Fezzik.destination :vagrant do
  set :domain, "..."
  Fezzik.env :unicorn_workers, 2
end

This syntax is shorter (you don't need an "options.each" block in each destination) but more importantly, it's DRY built directly into Fezzik. As we've seen, people are lazy when defining configuration and by default copy and paste things, even when they could use Ruby to make it more DRY. When the tool itself promotes good behavior, it's more likely to be used.

If you try to use Fezzik.env outside of a destination block, Fez throws an exception:

Please specify the server domain via the :domain variable
    /Users/philc/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/rake-remote_task-2.0.6/lib/rake/remote_task.rb:311:in `block in mandatory'
/Users/philc/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/rake-remote_task-2.0.6/lib/rake/remote_task.rb:268:in `call'
/Users/philc/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/rake-remote_task-2.0.6/lib/rake/remote_task.rb:268:in `block in fetch'
/Users/philc/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/rake-remote_task-2.0.6/lib/rake/remote_task.rb:321:in `block in protect_env'
<internal:prelude>:10:in `synchronize'
/Users/philc/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/rake-remote_task-2.0.6/lib/rake/remote_task.rb:320:in `protect_env'
/Users/philc/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/rake-remote_task-2.0.6/lib/rake/remote_task.rb:266:in `fetch'
/Users/philc/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/rake-remote_task-2.0.6/lib/rake/remote_task.rb:429:in `block in set'
/Users/philc/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/fezzik-0.6.2/lib/fezzik/environment.rb:4:in `env'

philc avatar May 06 '12 23:05 philc

Yep, this feature makes sense. I'll look at building it when I clean up the roles configuration, if I don't see a pull request before then.

dmac avatar May 06 '12 23:05 dmac

This and the role refactor will improve the user friendliness of Barkeep's deploy config significantly.

philc avatar May 06 '12 23:05 philc

Any new thoughts on this? I'm about to implement the good ol' env_hash and thought with all the new hotness this is the next biggest offender.

philc avatar Nov 29 '12 01:11 philc

Another idea is supporting multiple targets in Fezzik.destination:

Fezzik.destination :prod1, :prod2, :prod3 do
  Fezzik.env "var", "value"
end

philc avatar Nov 29 '12 01:11 philc

Woo ha ha!

philc avatar Nov 29 '12 06:11 philc

Great suggestion, and supporting multiple arguments is much simpler from an implementation perspective. Feature is live in version 0.7.4!

dmac avatar Nov 29 '12 06:11 dmac

This is a little klunky in that you can't define this block full of environment vars before you've used Fezzik.destination with a domain variable:

Please specify the server domain via the :domain variable

Will this be fixable once we get off of rake remote task?

philc avatar Dec 06 '12 21:12 philc

The main complexity comes from the ability to define environment variables per-host. What happens now is that if you declare an env without specifying hosts it gets added to every host in domain. If domain isn't defined we'd need to save these settings in a temporary envs_for_all_domains hash, then add them to each domain once it is defined. So more annoying than difficult, but something I'd rather avoid doing unless this continues to be a problem.

Is it very annoying to define your destinations like this:

destination :staging do
  # envs for staging
end

destination :prod do
  # envs for prod
end

destination :staging, :prod do
  # common envs
end

rather than the other way round?

dmac avatar Dec 06 '12 22:12 dmac

My natural expectation when reading code is to see the general params which apply to all hosts first, and then specialized configs further down the file. I wouldn't call it an annoyance; it just defied my up-front expectation and so I thought I'd mention it.

philc avatar Dec 06 '12 22:12 philc

On second thought, I'd say this is unintuitive enough that it's worth supporting the definition in the way I described. My mental model of these env vars is maps, and with the current implementation you can't define some general key/value pairs and change just one of them on a per-destination basis. We do that commonly in Ooyala's deploys. For example this scenario does not work:

destination :vagrant, :prod1, :prod2 do
  Fezzik.env "db_user", "ubuntu"
  Fezzik.env "rack_env", "production"
end

destination :vagrant do
  set domain, "my-vagrant"
  Fezzik.env "db_user", "vagrant"
end

...

philc avatar Dec 06 '12 22:12 philc

Fair enough, although that's a somewhat orthogonal example, given that your example would work if you had this first:

destination :vagrant do
  domain "vagrant"
end

dmac avatar Dec 06 '12 22:12 dmac

But I definitely see the advantage here. I'll reopen this ticket and think about it.

dmac avatar Dec 06 '12 22:12 dmac

Is this hard to implement now?

cespare avatar Oct 09 '13 21:10 cespare

No harder than it was before, I think.

dmac avatar Oct 18 '13 10:10 dmac