module TddDeploy::Environ
== module TddDeploy::Environ provides management for host provisioning and deployment variables - aka 'the environment'. The 'enviornment' - as used here - is not the Ruby ENV hash three types of variables are supported :int - which are integers :string - which are strings :list - which are input as comma separated strings [the actual separator is /[\s,]+/ ] which are used internally as arrays of strings. So, to set a :list variable you write 'foo = bar,baz,boop', and when you use it you get back ['bar', 'baz', 'boop'] (this inanity was done so we could save the environment as a simple ascii file) Environment variables: All environment variables can be read or set via accessors - which are created dynamically. They do _not_ show up in the Yard doc. === Integer variables * 'ssh_timeout' - timeout in seconds used to terminate remote commands fired off by ssh * 'site_base_port' - Base port for site servers. For example, if a pack of mongrels or thin
servers provide the rails end of the web site, they listen on 'site_base_port', +1, etd
* 'site_num_servers' - number of mongrels or thins to spin up === String variables * 'host_admin' - user name used on remote hosts. Should not be root, but should be in /etc/sudoers * 'local_admin' - user name of on local hosts which can ssh into remote hosts via public key authentication * 'local_admin_email' - email of local admin who should receive monitoring emails * 'site' - name of site This should satisfy /[a-z][a-z0-9_]*. * 'site_app_root' - the absolute path to DocumentRoot for the site * 'site_doc_root' - the absolute path to DocumentRoot for the site * 'site_special_dir' - absolute path to site special directory - for system configuration fragments, commands, etc * 'site_url' - the url for the site (w/o scheme - as in 'www.foo.com') * 'site_aliases' - aliases for the site. The delimiters will depend on your web server * 'site_user' - name of site user. TddDeploy assumes that each site will have a unique user on the remote host. this makes it easy to tell nginx and monit where to find configuration files for each site [both know how to included globbed paths] === List Variables * 'app_hosts' - hosts running a ruby app which must have a ruby available and will be served
via a reverse proxy
* 'balance_hosts' - load balancing servers [may be empty, in which case 'hosts' is used] * 'db_hosts' - hosts which run the database server [may be empty, in which case 'hosts' is used] * 'web_hosts' - hosts which run the web server [may be empty, in which case 'hosts' is used] * 'capfile_paths' - relative paths to capistrano recipe files. Defaults to './config/deploy.rb' === Pseudo Variables * 'hosts' - list of all hosts - always returns app_hosts + balance_hosts + db_hosts + web_hosts. may be assigned to if all three host lists are identical, otherwise raises an exception.
'tdd_deploy_context' hides it from view unless it can be assigned
=== Capfile Variables - Read Only * 'app' - list of all hosts in the :app role of the Capistrano recipes * 'db' - list of all hosts in the :db role of the Capistrano recipes * 'migration_hosts' - list of all hosts in the :db role with option :primary => true
of the Capistrano recipes
* 'web' - list of all hosts in the :web role of the Capistrano recipes
Constants
- ENV_FNAME
Public Instance Methods
# File lib/tdd_deploy/environ.rb, line 96 def capfile raise ::RuntimeError.new('Attempt to access capfile data w/o capfile_paths defined') unless DataCache.env_hash['capfile_paths'] unless DataCache.capfile DataCache.capfile = TddDeploy::Capfile.new DataCache.env_hash['capfile_paths'].each do |path| DataCache.capfile.load_recipes path end end DataCache.capfile end
clears the environment hash. Really - it's useless until re-initialized
# File lib/tdd_deploy/environ.rb, line 243 def clear_env DataCache.env_hash = {} end
Hash of default values - which are hokey
# File lib/tdd_deploy/environ.rb, line 201 def env_defaults DataCache.env_defaults end
# File lib/tdd_deploy/environ.rb, line 205 def env_desc DataCache.env_desc end
set up all the standard accessors lazy initialize DataCache.env_hash
# File lib/tdd_deploy/environ.rb, line 80 def env_hash read_env || reset_env unless defined?(DataCache.env_hash) DataCache.env_hash end
# File lib/tdd_deploy/environ.rb, line 85 def env_hash=(hash) raise ::ArgumentError.new("env_hash=(): arg must be a hash") unless hash.is_a? Hash if !(tmp = hash.keys - DataCache.env_types.keys).empty? raise ::ArgumentError.new("env_hash=(): Illegal Keys in value: #{tmp.join(',')}") elsif !(tmp = DataCache.env_types.keys - hash.keys).empty? raise ::ArgumentError.new("env_hash=(): Missing Keys in value: #{tmp.join(',')}") else DataCache.env_hash = hash end end
Hash mapping environment variable to type
# File lib/tdd_deploy/environ.rb, line 196 def env_types DataCache.env_types end
accessors for all defined env variables
# File lib/tdd_deploy/environ.rb, line 396 def hosts (self.web_hosts.to_a + self.db_hosts.to_a + self.balance_hosts.to_a + self.app_hosts.to_a).uniq.sort end
# File lib/tdd_deploy/environ.rb, line 400 def hosts=(list) if (self.web_hosts.nil? && self.db_hosts.nil?) || self.web_hosts == self.db_hosts self.web_hosts = self.db_hosts = self.app_hosts = self.str_to_list(list) self.balance_hosts = [] else raise ::RuntimeError.new("Cannot assign value to 'hosts' if web_hosts &/or db_hosts already set.\n web_hosts: #{self.web_hosts}\n db_hosts: #{self.db_hosts}") end end
packs an array into a comma separated string
# File lib/tdd_deploy/environ.rb, line 318 def list_to_str key tmp = self.env_hash[key] tmp.is_a?(Array) ? tmp.join(',') : tmp.to_s end
# File lib/tdd_deploy/environ.rb, line 411 def migration_hosts self.capfile.migration_host_list end
takes the name of a list (as a string or symbol), a single string, or an array of host names. If it's an array, then returns uniq-ified array of strings [to handle the uniion of lists]
# File lib/tdd_deploy/environ.rb, line 417 def rationalize_host_list(host_list_or_list_name) if host_list_or_list_name.is_a? String return self.respond_to?(host_list_or_list_name.to_sym) ? self.send(host_list_or_list_name.to_sym) : [host_list_or_list_name] elsif host_list_or_list_name.is_a? Symbol return self.respond_to?(host_list_or_list_name) ? self.send(host_list_or_list_name) : [host_list_or_list_name.to_s] elsif host_list_or_list_name.is_a? Array return host_list_or_list_name.map { |host| host.to_s }.uniq else raise ArgumentError.new("rationalize_host_list(#{host_list_or_list_name.inspect}) is invalid") end end
reads the environment from TddDeploy::Environ::ENV_FNAME
(site_host_setup.env) if the file exists someplace between the current directory and the root of the filesystem
# File lib/tdd_deploy/environ.rb, line 255 def read_env dir_path = Dir.pwd DataCache.env_hash ||= {} loop do path = File.join dir_path, TddDeploy::Environ::ENV_FNAME if File.exists? TddDeploy::Environ::ENV_FNAME line_no = 0 if f = File.new(path, 'r') begin f.each do |line| line_no += 1 if line =~ /^\s*(\w+)\s*=\s*(.*?)\s*$/ key = $1.downcase if self.env_types.keys.include? key self.send "#{key}=".to_sym, $2 # self.env_hash[key] = self.env_types[key] == :list ? self.str_to_list($2) : $2.to_s else raise ::ArugmentError.new("TddDeploy::Environ#read_env: Error in #{TddDeploy::Error::ENV_FNAME}: #{line_no}: Illegal Key: #{key}") end else raise ::ArugmentError.new("TddDeploy::Environ#read_env: Error in #{TddDeploy::Error::ENV_FNAME}: #{line_no}: Unmatched Line: #{line}}") end end ensure f.close end # add any missing env keys (self.env_types.keys - self.env_hash.keys).each do |key| case self.env_types[key] when :pseudo then next when :capfile then next when :list self.env_hash[key] = str_to_list(self.env_defaults[key]) else self.env_hash[key] = self.env_defaults[key] end end return self.env_hash else raise ::RuntimeError.new("Unable to open #{path} for reading") end elsif dir_path.length <= 1 # reached root level, so initialize to defaults and exit return nil else # move to parent directory dir_path = File.expand_path('..', dir_path) end end nil end
reset_env
resets env_hash
to env_defaults
# File lib/tdd_deploy/environ.rb, line 248 def reset_env clear_env set_env self.env_defaults end
saves the current environment in the current working directory in the file 'site_host_setup.env' [aka TddDeploy::Environ::ENV_FNAME]
# File lib/tdd_deploy/environ.rb, line 326 def save_env f = File.new(TddDeploy::Environ::ENV_FNAME, "w") self.env_types.keys.sort.each do |k| v = self.env_hash[k] || '' case self.env_types[k] when :int then f.write "#{k}=#{v}\n" when :string then f.write "#{k}=#{v}\n" when :list then f.write "#{k}=#{self.list_to_str(k)}\n" unless k == 'hosts' when :pseudo then next when :capfile then next else raise ::RuntimeError.new("unknown key: #{k}") end end f.close end
set_env
(value_hash {}) - convenience method which sets values of the environment hash using a hash rather than one-at-a-time
# File lib/tdd_deploy/environ.rb, line 211 def set_env(value_hash = {}) DataCache.env_hash ||= {} value_hash.each do |k, v| k = k.to_s case self.env_types[k] when :int then DataCache.env_hash[k] = v.to_i when :string then DataCache.env_hash[k] = v.to_s when :list then DataCache.env_hash[k] = self.str_to_list(v) when :capfile then next when :pseudo then if k == 'hosts' if (tmp = DataCache.env_hash['web_hosts']) == DataCache.env_hash['db_hosts'] \ && [] == DataCache.env_hash['balance_hosts'] \ && tmp == DataCache.env_hash['app_hosts'] DataCache.env_hash['web_hosts'] = DataCache.env_hash['db_hosts'] = DataCache.env_hash['app_hosts'] = self.str_to_list(v) DataCache.env_hash['balance_hosts'] = [] else raise ::RuntimeError.new("#{self}#reset_env(): Cannot assign value to 'hosts' if web_hosts &/or db_hosts already set.\n web_hosts: #{DataCache.env_hash['web_hosts']}\n db_hosts: #{DataCache.env_hash['db_hosts']}") # raise RuntimeError.new("Cannot change hosts key if web_hosts != db_hosts") end else next end else raise ::ArgumentError.new("#{self}#reset_env(): Illegal environment key: #{k}") end end end
bursts comma/space separated string into a sorted, unique array
# File lib/tdd_deploy/environ.rb, line 308 def str_to_list str case when str.is_a?(String) then str.split(/[\s,]+/).uniq.sort when str.is_a?(Array) then str.uniq.sort else raise ::ArgumentError.new("str_to_list: #{str}") end end