machines 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (180) hide show
  1. data/.gitignore +11 -0
  2. data/EXAMPLES.md +18 -0
  3. data/Gemfile +4 -0
  4. data/Guardfile +14 -0
  5. data/INSTALL.md +25 -0
  6. data/LICENSE +23 -0
  7. data/README.md +271 -0
  8. data/Rakefile +60 -0
  9. data/TODO.md +92 -0
  10. data/bin/machines +6 -0
  11. data/lib/machines/app_settings.rb +54 -0
  12. data/lib/machines/base.rb +13 -0
  13. data/lib/machines/checks.rb +63 -0
  14. data/lib/machines/cloud_machine.rb +33 -0
  15. data/lib/machines/command.rb +86 -0
  16. data/lib/machines/commandline.rb +148 -0
  17. data/lib/machines/configuration.rb +49 -0
  18. data/lib/machines/core.rb +117 -0
  19. data/lib/machines/database.rb +17 -0
  20. data/lib/machines/file_operations.rb +104 -0
  21. data/lib/machines/help.rb +30 -0
  22. data/lib/machines/installation.rb +151 -0
  23. data/lib/machines/log_command.rb +22 -0
  24. data/lib/machines/logger.rb +65 -0
  25. data/lib/machines/machinesfile.rb +25 -0
  26. data/lib/machines/named_buffer.rb +9 -0
  27. data/lib/machines/questions.rb +15 -0
  28. data/lib/machines/services.rb +24 -0
  29. data/lib/machines/upload.rb +29 -0
  30. data/lib/machines/version.rb +4 -0
  31. data/lib/machines.rb +19 -0
  32. data/lib/packages/abiword.rb +11 -0
  33. data/lib/packages/amazon_mp3.rb +4 -0
  34. data/lib/packages/awstats.rb +16 -0
  35. data/lib/packages/base.rb +14 -0
  36. data/lib/packages/chrome.rb +12 -0
  37. data/lib/packages/cruisecontrol.rb +22 -0
  38. data/lib/packages/dependencies.rb +10 -0
  39. data/lib/packages/docky.rb +36 -0
  40. data/lib/packages/dotfiles.rb +26 -0
  41. data/lib/packages/file_roller.rb +12 -0
  42. data/lib/packages/finalise.rb +4 -0
  43. data/lib/packages/firefox.rb +4 -0
  44. data/lib/packages/gedit.rb +11 -0
  45. data/lib/packages/git.rb +4 -0
  46. data/lib/packages/gmate.rb +33 -0
  47. data/lib/packages/gnome.rb +10 -0
  48. data/lib/packages/gnumeric.rb +11 -0
  49. data/lib/packages/hosts.rb +13 -0
  50. data/lib/packages/load_machines.rb +38 -0
  51. data/lib/packages/monit.rb +10 -0
  52. data/lib/packages/mysql.rb +46 -0
  53. data/lib/packages/nginx.rb +22 -0
  54. data/lib/packages/nginx_logrotate.rb +26 -0
  55. data/lib/packages/openbox.rb +35 -0
  56. data/lib/packages/passenger.rb +14 -0
  57. data/lib/packages/passenger_nginx.rb +8 -0
  58. data/lib/packages/postfix.rb +10 -0
  59. data/lib/packages/questions.rb +5 -0
  60. data/lib/packages/rbenv.rb +27 -0
  61. data/lib/packages/rvm.rb +20 -0
  62. data/lib/packages/save_machines.rb +4 -0
  63. data/lib/packages/slim.rb +6 -0
  64. data/lib/packages/sqlserver.rb +5 -0
  65. data/lib/packages/subtle.rb +29 -0
  66. data/lib/packages/sudo_mods.rb +6 -0
  67. data/lib/packages/time.rb +6 -0
  68. data/lib/packages/time_daily.rb +5 -0
  69. data/lib/packages/timezone.rb +10 -0
  70. data/lib/packages/unison.rb +5 -0
  71. data/lib/packages/virtualbox.rb +11 -0
  72. data/lib/packages/virtualbox_guest.rb +7 -0
  73. data/lib/packages/webapps.rb +36 -0
  74. data/lib/template/Machinesfile +48 -0
  75. data/lib/template/certificates/example.com.crt +0 -0
  76. data/lib/template/certificates/example.com.key +0 -0
  77. data/lib/template/certificates/selfsigned.crt +14 -0
  78. data/lib/template/certificates/selfsigned.key +16 -0
  79. data/lib/template/config.yml +98 -0
  80. data/lib/template/logrotate/app.erb +10 -0
  81. data/lib/template/logrotate/nginx.erb +12 -0
  82. data/lib/template/machines.yml +179 -0
  83. data/lib/template/misc/awstats.conf.erb +7 -0
  84. data/lib/template/misc/ntp.conf +7 -0
  85. data/lib/template/monit/conf.d/delayed_job.erb +11 -0
  86. data/lib/template/monit/conf.d/mysql.erb +7 -0
  87. data/lib/template/monit/conf.d/nginx +5 -0
  88. data/lib/template/monit/conf.d/postfix +7 -0
  89. data/lib/template/monit/conf.d/ssh +6 -0
  90. data/lib/template/monit/conf.d/system.erb +14 -0
  91. data/lib/template/monit/monitrc.erb +10 -0
  92. data/lib/template/monit/upstart.conf +16 -0
  93. data/lib/template/mysql/dbmaster.cnf +7 -0
  94. data/lib/template/mysql/dbslave.cnf +3 -0
  95. data/lib/template/nginx/app_server.conf.erb +87 -0
  96. data/lib/template/nginx/nginx.conf.erb +46 -0
  97. data/lib/template/nginx/upstart.conf.erb +21 -0
  98. data/lib/template/packages/custom.rb +17 -0
  99. data/lib/template/packages/productivity.rb +18 -0
  100. data/lib/template/slim/themes/dark/background.jpg +0 -0
  101. data/lib/template/slim/themes/dark/panel.png +0 -0
  102. data/lib/template/slim/themes/dark/slim.theme +39 -0
  103. data/lib/template/users/phil/dotfiles/bash_aliases +45 -0
  104. data/lib/template/users/phil/dotfiles/config/Trolltech.conf +4 -0
  105. data/lib/template/users/phil/dotfiles/config/gtk-3.0/settings.ini +9 -0
  106. data/lib/template/users/phil/dotfiles/config/openbox/autostart.sh +14 -0
  107. data/lib/template/users/phil/dotfiles/config/openbox/rc.xml +482 -0
  108. data/lib/template/users/phil/dotfiles/config/terminator/config +10 -0
  109. data/lib/template/users/phil/dotfiles/fonts.conf +15 -0
  110. data/lib/template/users/phil/dotfiles/gitconfig +27 -0
  111. data/lib/template/users/phil/dotfiles/gtkrc-2.0 +16 -0
  112. data/lib/template/users/phil/dotfiles/local/share/applications/mimeapps.list +4 -0
  113. data/lib/template/users/phil/dotfiles/unison/default.prf +33 -0
  114. data/lib/template/users/www/authorized_keys +0 -0
  115. data/lib/template/users/www/dotfiles/bash_aliases +40 -0
  116. data/lib/template/webapps.yml +75 -0
  117. data/machines.gemspec +44 -0
  118. data/spec/acceptance/dev_machine_spec.rb +22 -0
  119. data/spec/lib/machines/app_settings_spec.rb +106 -0
  120. data/spec/lib/machines/checks_spec.rb +105 -0
  121. data/spec/lib/machines/cloud_machine_spec.rb +36 -0
  122. data/spec/lib/machines/command_spec.rb +184 -0
  123. data/spec/lib/machines/commandline_spec.rb +299 -0
  124. data/spec/lib/machines/configuration_spec.rb +61 -0
  125. data/spec/lib/machines/core_spec.rb +299 -0
  126. data/spec/lib/machines/database_spec.rb +51 -0
  127. data/spec/lib/machines/file_operations_spec.rb +124 -0
  128. data/spec/lib/machines/help_spec.rb +22 -0
  129. data/spec/lib/machines/installation_spec.rb +176 -0
  130. data/spec/lib/machines/log_command_spec.rb +16 -0
  131. data/spec/lib/machines/logger_spec.rb +70 -0
  132. data/spec/lib/machines/machinesfile_spec.rb +34 -0
  133. data/spec/lib/machines/questions_spec.rb +73 -0
  134. data/spec/lib/machines/services_spec.rb +26 -0
  135. data/spec/lib/machines/upload_spec.rb +86 -0
  136. data/spec/lib/packages/abiword_spec.rb +20 -0
  137. data/spec/lib/packages/amazon_mp3_spec.rb +17 -0
  138. data/spec/lib/packages/awstats_spec.rb +26 -0
  139. data/spec/lib/packages/base_spec.rb +21 -0
  140. data/spec/lib/packages/chrome_spec.rb +30 -0
  141. data/spec/lib/packages/cruisecontrol_spec.rb +33 -0
  142. data/spec/lib/packages/dependencies_spec.rb +20 -0
  143. data/spec/lib/packages/docky_spec.rb +32 -0
  144. data/spec/lib/packages/dotfiles_spec.rb +44 -0
  145. data/spec/lib/packages/file_roller_spec.rb +69 -0
  146. data/spec/lib/packages/firefox_spec.rb +16 -0
  147. data/spec/lib/packages/gedit_spec.rb +20 -0
  148. data/spec/lib/packages/git_spec.rb +16 -0
  149. data/spec/lib/packages/gmate_spec.rb +39 -0
  150. data/spec/lib/packages/gnome_spec.rb +22 -0
  151. data/spec/lib/packages/gnumeric_spec.rb +21 -0
  152. data/spec/lib/packages/hosts_spec.rb +41 -0
  153. data/spec/lib/packages/load_machines_spec.rb +118 -0
  154. data/spec/lib/packages/monit_spec.rb +34 -0
  155. data/spec/lib/packages/mysql_spec.rb +69 -0
  156. data/spec/lib/packages/nginx_logrotate_spec.rb +80 -0
  157. data/spec/lib/packages/nginx_spec.rb +46 -0
  158. data/spec/lib/packages/openbox_spec.rb +41 -0
  159. data/spec/lib/packages/passenger_nginx_spec.rb +20 -0
  160. data/spec/lib/packages/passenger_spec.rb +26 -0
  161. data/spec/lib/packages/postfix_spec.rb +19 -0
  162. data/spec/lib/packages/questions_spec.rb +29 -0
  163. data/spec/lib/packages/rbenv_spec.rb +32 -0
  164. data/spec/lib/packages/rvm_spec.rb +31 -0
  165. data/spec/lib/packages/save_machines_spec.rb +51 -0
  166. data/spec/lib/packages/slim_spec.rb +22 -0
  167. data/spec/lib/packages/sqlserver_spec.rb +17 -0
  168. data/spec/lib/packages/timezone_spec.rb +27 -0
  169. data/spec/lib/packages/unison_spec.rb +17 -0
  170. data/spec/lib/packages/virtualbox_guest_spec.rb +25 -0
  171. data/spec/lib/packages/virtualbox_spec.rb +23 -0
  172. data/spec/lib/packages/webapps_spec.rb +70 -0
  173. data/spec/spec_helper.rb +103 -0
  174. data/spec/support/coverage.rb +8 -0
  175. data/spec/support/fake_out.rb +22 -0
  176. data/spec/support/fakefs_additions.rb +10 -0
  177. data/spec/support/minitest.rb +69 -0
  178. data/spec/support/vm_control.rb +54 -0
  179. data/tmp/.gitkeep +0 -0
  180. metadata +581 -0
@@ -0,0 +1,33 @@
1
+ module Machines
2
+ module CloudMachine
3
+ def connect_to_cloud
4
+ begin
5
+ require 'fog'
6
+ rescue LoadError
7
+ say 'fog gem required to use cloud features.'
8
+ say 'Please "gem install fog".'
9
+ raise
10
+ end
11
+
12
+ Fog.credential = 'machines_key'
13
+ options = symbolize_keys($conf.cloud.to_hash)
14
+ options.merge!(:region => $conf.machine.cloud.region)
15
+ $conf.cloud.connection = Fog::Compute.new(options)
16
+ end
17
+
18
+ def create_server
19
+ server = $conf.cloud.connection.servers.create(
20
+ :private_key_path => $conf.machine.cloud.private_key_path,
21
+ :public_key_path => $conf.machine.cloud.public_key_path,
22
+ :username => $conf.machine.cloud.username,
23
+ :flavor_id => $conf.machine.cloud.flavor_id,
24
+ :image_id => $conf.machine.cloud.image_id)
25
+ server.wait_for { ready? }
26
+ end
27
+
28
+ def symbolize_keys hash
29
+ hash.inject({}){|new_hash, (k, v)| new_hash[k.to_sym] = v; new_hash }
30
+ end
31
+ end
32
+ end
33
+
@@ -0,0 +1,86 @@
1
+ require 'machines/logger'
2
+
3
+ module Machines
4
+ class Command
5
+ class << self
6
+ attr_accessor :file, :debug, :console
7
+ end
8
+
9
+ attr_accessor :command, :check
10
+
11
+ def self.scp= scp
12
+ @@scp = scp
13
+ end
14
+
15
+ def self.ssh= ssh
16
+ @@ssh = ssh
17
+ end
18
+
19
+ def self.ssh
20
+ @@ssh
21
+ end
22
+
23
+ def self.scp
24
+ @@scp
25
+ end
26
+
27
+ def initialize(command, check)
28
+ @command = command
29
+ @check = check
30
+ @sudo = false
31
+ end
32
+
33
+ def use_sudo
34
+ @sudo = 'sudo'
35
+ end
36
+
37
+ def run
38
+ process {Command.file.log @@ssh.exec! wrap_in_export_and_sudo(@command)}
39
+ end
40
+
41
+ def info
42
+ ("%-6s " % (@sudo ? 'SUDO' : 'RUN')) + command
43
+ end
44
+
45
+ protected
46
+ def progress
47
+ "%3d%% " % (($conf.commands.index(self) + 1) / $conf.commands.count.to_f * 100).round
48
+ end
49
+
50
+ def process &block
51
+ Command.console.log progress + info, :newline => ($conf.log_only || false)
52
+ Command.file.log info, :color => :highlight
53
+ unless $conf.log_only
54
+ begin
55
+ yield
56
+ result = @@ssh.exec!(wrap_in_export_and_sudo(@check))
57
+ result = check_result(result || '')
58
+ color = color_for(result)
59
+ Command.file.log result, :color => color
60
+ Command.console.log progress + info, :color => color
61
+ rescue Exception => e
62
+ Command.console.log(progress + info, :color => :failure) rescue nil
63
+ Command.file.log(e.to_s, :color => :failure) rescue nil
64
+ raise e
65
+ end
66
+ end
67
+ end
68
+
69
+ def wrap_in_export_and_sudo command
70
+ command = "export TERM=linux && #{command}"
71
+ echo_password = "echo #{$conf.password} | " if $conf.password
72
+ command = "#{echo_password}sudo -S bash -c '#{command}'" if @sudo
73
+ Command.debug.log command
74
+ command
75
+ end
76
+
77
+ def check_result result
78
+ result.scan(/CHECK PASSED|CHECK FAILED/).first || 'NOT CHECKED'
79
+ end
80
+
81
+ def color_for result
82
+ {'NOT CHECKED' => :warning, 'CHECK FAILED' => :failure, 'CHECK PASSED' => :success}[result]
83
+ end
84
+ end
85
+ end
86
+
@@ -0,0 +1,148 @@
1
+ module Machines
2
+ module Commandline
3
+ # Loads Machinesfile, opens an SCP connection and runs all commands and file uploads
4
+ def build options
5
+ $conf.machine_name = options.shift
6
+ $conf.task = options.shift
7
+ init
8
+ load_machinesfile
9
+
10
+ task $conf.task.to_sym if $conf.task
11
+
12
+ ssh_options = {:paranoid => false}
13
+ if $conf.machine.cloud
14
+ username = $conf.machine.cloud.username
15
+ ssh_options[:keys] = [$conf.machine.cloud.private_key_path]
16
+ else
17
+ username = $conf.machine.user
18
+ ssh_options[:password] = $conf.password
19
+ end
20
+
21
+ if $conf.log_only
22
+ $conf.commands.each do |command|
23
+ command.run
24
+ end
25
+ else
26
+ Kernel.trap("INT") { prepare_to_exit }
27
+ begin
28
+ Command.ssh = Net::SSH.start $conf.machine.address, username, ssh_options
29
+ Command.scp = Net::SCP.new(Command.ssh)
30
+ $conf.commands.each do |command|
31
+ command.run
32
+ Command.file.flush
33
+ exit if $exit_requested
34
+ end
35
+ ensure
36
+ Command.ssh.close
37
+ end
38
+ end
39
+ end
40
+
41
+ def dryrun options
42
+ $conf.log_only = true
43
+ build options
44
+ end
45
+
46
+ # Execute a given command e.g. dryrun, build, generate, htpasswd, packages, override, tasks
47
+ def execute(options)
48
+ help = Help.new
49
+ action = options.shift
50
+ if help.actions.include?(action)
51
+ action = 'generate' if action == 'new'
52
+ send action, options
53
+ else
54
+ say help.syntax
55
+ end
56
+ end
57
+
58
+ def generate options
59
+ dir = options.first || './'
60
+ if File.exists? dir
61
+ confirm = ask "Overwrite '#{dir}' (y/n)? "
62
+ return unless confirm.downcase == 'y'
63
+ end
64
+ FileUtils.cp_r(File.join($conf.application_dir, 'template', '/.'), dir)
65
+ FileUtils.mkdir_p(File.join(dir, 'packages'))
66
+ say "Project created at #{dir}/"
67
+ end
68
+
69
+ def htpasswd options
70
+ path = File.join($conf.webserver, 'conf', 'htpasswd')
71
+ say "Generate BasicAuth password and add to #{path}"
72
+ username = ask('Username: ')
73
+ password = enter_password 'users'
74
+
75
+ crypted_pass = password.crypt(WEBrick::Utils.random_string(2))
76
+ FileUtils.mkdir_p File.dirname(path)
77
+ File.open(path, 'a') {|file| file.puts "#{username}:#{crypted_pass}" }
78
+ say "Password encrypted and added to #{path}"
79
+ end
80
+
81
+ def init
82
+ $exit_requested = false
83
+ $conf.passwords = []
84
+ $conf.commands = []
85
+ $conf.tasks = {}
86
+ $conf.load('config.yml')
87
+
88
+ Command.file ||= Machines::Logger.new File.open('log/output.log', 'w')
89
+ Command.debug ||= Machines::Logger.new File.open('log/debug.log', 'w')
90
+ Command.console ||= Machines::Logger.new STDOUT, :truncate => true
91
+ end
92
+
93
+ def load_machinesfile
94
+ eval File.read('Machinesfile'), nil, "eval: Machinesfile"
95
+ rescue LoadError => e
96
+ if e.message =~ /Machinesfile/
97
+ raise LoadError, "Machinesfile does not exist. Use `machines new <DIR>` to create a template."
98
+ else
99
+ raise
100
+ end
101
+ end
102
+
103
+ def packages notused
104
+ say 'Default packages'
105
+ Dir[File.join($conf.application_dir, 'packages', '**/*.rb')].each do |package|
106
+ say " * #{File.basename(package, '.rb')}"
107
+ end
108
+ say ''
109
+
110
+ say 'Project packages'
111
+ Dir[File.join('packages', '**/*.rb')].each do |package|
112
+ say " * #{File.basename(package, '.rb')}"
113
+ end
114
+ end
115
+
116
+ def override package
117
+ package = package.first
118
+ destination = File.join('packages', "#{package}.rb")
119
+ answer = File.exists?(destination) ? ask('Project package already exists. Overwrite? (y/n)') : 'y'
120
+ if answer == 'y'
121
+ source = File.join($conf.application_dir, 'packages', "#{package}.rb")
122
+ FileUtils.cp(source, destination)
123
+ say "Package copied to #{destination}"
124
+ else
125
+ say 'Aborted.'
126
+ end
127
+ end
128
+
129
+ def tasks
130
+ $conf.log_only = true
131
+ init
132
+ load_machinesfile
133
+ say 'Tasks'
134
+ $conf.tasks.each do |task_name, settings|
135
+ say " %-20s #{settings[:description]}" % task_name
136
+ end
137
+ end
138
+
139
+ private
140
+ def prepare_to_exit
141
+ exit if $exit_requested
142
+ $exit_requested = true
143
+ Command.console.log("\nEXITING after current command completes...", :color => :warning)
144
+ Command.console.log("(Press again to terminate immediately)...", :color => :warning)
145
+ end
146
+ end
147
+ end
148
+
@@ -0,0 +1,49 @@
1
+ module Machines
2
+ module Configuration
3
+ # Add a new user
4
+ # (uses the lowlevel useradd so doesn't set a password unless specified)
5
+ # @param [String] login User name to create
6
+ # @param [Hash] options
7
+ # @option options [String] :password
8
+ # @option options [Boolean] :admin Adds the user to the admin group when true
9
+ def add_user login, options = {}
10
+ password = "-p #{`openssl passwd #{options[:password]}`.gsub("\n", '')} " if options[:password]
11
+ admin = "-G admin " if options[:admin]
12
+ Command.new(
13
+ "useradd -s /bin/bash -d /home/#{login} -m #{password}#{admin}#{login}",
14
+ check_dir("/home/#{login}")
15
+ )
16
+ end
17
+
18
+ # Add an existing user to a secondary group
19
+ # @param [Hash] options
20
+ # @option options [String] :user The user to add
21
+ # @option options [String] :to Adds an existing user to the specified group
22
+ def add options
23
+ required_options options, [:user, :to]
24
+ Command.new("usermod -a -G #{options[:to]} #{options[:user]}", check_command("groups #{options[:user]}", options[:to]))
25
+ end
26
+
27
+ # Sets gconf key value pairs
28
+ # @param [Hash] options One or many key/value pairs to set
29
+ def configure options
30
+ options.map do |key, value|
31
+ types = {String => 'string', Fixnum => 'int', TrueClass => 'bool',
32
+ FalseClass => 'bool', Float => 'float', Array => 'list --list-type=string'}
33
+ type = types[value.class]
34
+ raise 'Invalid type for configure' unless type
35
+ value = value.to_json if value.is_a?(Array)
36
+ value = %("#{value}") if type == 'string'
37
+ check = "gconftool-2 --get \"#{key}\" | grep #{value} #{echo_result}"
38
+ Command.new("gconftool-2 --set \"#{key}\" --type #{type} #{value}", check)
39
+ end
40
+ end
41
+
42
+ # Removes a user, home and any other related files
43
+ # @param [String] login User name to remove
44
+ def del_user login
45
+ Command.new("deluser #{login} --remove-home -q", check_file('/home/login', false))
46
+ end
47
+ end
48
+ end
49
+
@@ -0,0 +1,117 @@
1
+ module Machines
2
+ module Core
3
+ # If a block is given, store the task, log it and run it
4
+ # If no block is given, sets commands to only those of the specified task so they can be run standalone
5
+ # @param [Symbol] name Name of the task
6
+ # @param [String] description Describe the task
7
+ # @param [Hash] options
8
+ # @option options [Symbol, Array] :if Dependent tasks that must already have been added for this task to be added
9
+ def task name, description = nil, options = {}, &block
10
+ if block
11
+ dependencies = [options[:if]].flatten
12
+ return if options[:if] && (dependencies - $conf.tasks.keys).any?
13
+ store_task name, description, &block
14
+ $conf.commands << LogCommand.new(name, description)
15
+ yield
16
+ else
17
+ $conf.commands = []
18
+ $conf.tasks[name][:block].call
19
+ end
20
+ end
21
+
22
+ def store_task name, description, &block
23
+ $conf.tasks[name] = {:description => description, :block => block}
24
+ end
25
+
26
+ def list_tasks
27
+ $conf.tasks.each do |name, task|
28
+ say " #{"%-20s" % name}#{task[:description]}"
29
+ end
30
+ end
31
+
32
+ def generate_password
33
+ WEBrick::Utils.random_string(20)
34
+ end
35
+
36
+ # Only executes the code if $conf parameters match what is given in args
37
+ def only options, &block
38
+ yield if matched(options)
39
+ end
40
+
41
+ # Does not execute the code if $conf parameters match what is given in args
42
+ def except options, &block
43
+ yield unless matched(options)
44
+ end
45
+
46
+ def matched options
47
+ options.each do |key, value|
48
+ value = value.is_a?(Array) ? value.map{|o| o.to_s } : value.to_s
49
+ if $conf[key].is_a?(Array)
50
+ values = $conf[key].map{|o| o.to_s }
51
+ if value.is_a?(Array)
52
+ return unless values.reject{ |symbol| !value.include?(symbol.to_s) }.any?
53
+ else
54
+ return unless values.include?(value)
55
+ end
56
+ else
57
+ if value.is_a?(Array)
58
+ return unless value.include?($conf[key].to_s)
59
+ else
60
+ return unless value == $conf[key].to_s
61
+ end
62
+ end
63
+ end
64
+ true
65
+ end
66
+
67
+ # Queue up command(s) to run remotely
68
+ # @param [Array] commands Command(s) to run.
69
+ # If first command is a string it creates a Command object using the first two strings as command and check
70
+ def run *commands
71
+ commands = command_from_string(commands)
72
+ $conf.commands += commands.flatten
73
+ end
74
+
75
+ # Queue up command(s) using SUDO to run remotely
76
+ # @param [Array] commands Command(s) to run
77
+ def sudo *commands
78
+ commands = command_from_string commands
79
+ commands.flatten.each do |command|
80
+ if command.is_a?(Upload)
81
+ temp_path = "/tmp/#{File.basename(command.remote)}"
82
+ dir_suffix = command.local.is_a?(String) && File.directory?(command.local) ? '/.' : ''
83
+ remote_dest = command.remote
84
+ command.remote = temp_path
85
+ command.check = check_file(temp_path)
86
+ run command
87
+ sudo copy(temp_path + dir_suffix, remote_dest)
88
+ run remove temp_path
89
+ else
90
+ command.use_sudo
91
+ run command
92
+ end
93
+ end
94
+ end
95
+
96
+ # Upload a file or folder using SCP
97
+ # Can be used with sudo or run
98
+ # @param [String] local_source File or folder on the local machine
99
+ # @param [String] remote_dest Folder on the remote machine to copy to
100
+ # upload 'source_dir', '~' #=> creates source_dir/subdir as ~/subdir
101
+ def upload local_source, remote_dest
102
+ Upload.new(local_source, remote_dest, check_file(remote_dest))
103
+ end
104
+
105
+ # Validate some methods that require certain options
106
+ def required_options options, required
107
+ required.each do |option|
108
+ raise ArgumentError, "Missing option '#{option}'. Check trace for location of the problem." unless options[option]
109
+ end
110
+ end
111
+
112
+ def command_from_string commands
113
+ commands.first.is_a?(String) ? [Command.new(commands[0], commands[1])] : commands
114
+ end
115
+ end
116
+ end
117
+
@@ -0,0 +1,17 @@
1
+ module Machines
2
+ module Database
3
+ # Write the database.yml file from webapps.yml
4
+ # @param [AppBuilder] app
5
+ def write_database_yml app
6
+ yml = {$conf.environment.to_s => {
7
+ 'adapter' => 'mysql',
8
+ 'database' => app.database || app.name,
9
+ 'username' => app.username || app.name,
10
+ 'password' => app.password,
11
+ 'host' => $conf.db_server.address,
12
+ 'encoding' => 'utf8'}}.to_yaml
13
+ write yml, :to => File.join(app.path, 'shared/config/database.yml'), :name => 'database.yml'
14
+ end
15
+ end
16
+ end
17
+
@@ -0,0 +1,104 @@
1
+ module Machines
2
+ module FileOperations
3
+ # Add a line of text to the end of a file unless it already exists
4
+ # @param [String] text Text to add
5
+ # @param [Hash] options
6
+ # @option options [String] :to File to append to
7
+ def append text, options
8
+ text = text.gsub(/([\\$"`])/, '\\\\\1')
9
+ Command.new("grep \"#{text}\" #{options[:to]} || echo \"#{text}\" >> #{options[:to]}", check_string(text, options[:to]))
10
+ end
11
+
12
+ # Change permissions of a path
13
+ # @param [String, Integer] mode chmod permissions to set
14
+ # @param [String] path Path to set
15
+ def chmod mode, path
16
+ Command.new("chmod #{mode} #{path}", check_perms(mode, path))
17
+ end
18
+
19
+ # Change ownership of a path
20
+ # @param [String] user sets user and group unless user:group is specified
21
+ # @param [String] path Path to set
22
+ # @param [Hash] options
23
+ # @option options [String] :recursive Chowns recursively if true
24
+ def chown user, path, options = {}
25
+ recursive = '-R ' if options[:recursive]
26
+ user = "#{user}:#{user}" unless user.index(':')
27
+ Command.new("chown #{recursive}#{user} #{path}", check_owner(user, path))
28
+ end
29
+
30
+ # Copy a remote file or folder (will overwrite)
31
+ # @param [String] from Existing path
32
+ # @param [String] to Path to copy to
33
+ def copy from, to
34
+ Command.new("cp -rf #{from} #{to}", check_file(to))
35
+ end
36
+
37
+ # Write a file from an ERB template
38
+ # @param [String] erb_path Path to the ERB file to process
39
+ # @param [Hash] options
40
+ # @option options [AppBuilder] :settings Contains the settings as OpenStruct method calls for calling from the template
41
+ # @option options [String] :to File to write to
42
+ def create_from erb_path, options
43
+ erb = ERB.new(File.read(erb_path), nil, '<>')
44
+ binding = options[:settings] ? options[:settings].get_binding : nil
45
+ options[:name] = erb_path
46
+ write erb.result(binding), options
47
+ end
48
+
49
+ # Add a symlink
50
+ # @param [String] target Existing path to link
51
+ # @param [String] link_name path name for the link
52
+ def link target, link_name
53
+ Command.new("ln -sf #{target} #{link_name}", check_link(link_name))
54
+ end
55
+
56
+ # Create a path or paths on the remote host (Uses -p to be safe and create full path)
57
+ # @param [String, Array] dirs A single path, multiple paths or array of paths to create
58
+ def mkdir *dirs
59
+ dirs.flatten.map do |dir|
60
+ Command.new("mkdir -p #{dir}", check_dir(dir))
61
+ end
62
+ end
63
+
64
+ # Rename a remote file or folder
65
+ # @param [String] oldname Existing filename
66
+ # @param [String] newname Rename to this =
67
+ def rename oldname, newname
68
+ Command.new("mv -f #{oldname} #{newname}", check_file(newname))
69
+ end
70
+
71
+ # Remove a remote file or folder
72
+ # @param [String] file or folder to remove (uses rm with -rf which ignores non-existent files and is recursive)
73
+ def remove file
74
+ Command.new("rm -rf #{file}", check_file(file, false))
75
+ end
76
+
77
+ # Take off the version numbers from a path name
78
+ # @param [String] name Name of the path to rename
79
+ def remove_version_info name
80
+ Command.new("find . -maxdepth 1 -name \"#{name}*\" -a -type d | xargs -I xxx mv xxx #{name}", check_file(name))
81
+ end
82
+
83
+ # Replace some text in a file
84
+ # @param [String] regex The expression to search for
85
+ # @param [Hash] options
86
+ # @option options [String] :with Text to use as the replacement
87
+ # @option options [String] :in Filename to replace text in
88
+ def replace regex, options
89
+ required_options options, [:with, :in]
90
+ with = options[:with].gsub(/([\n\/\\$"`])/, '\\\\\1')
91
+ Command.new("sed -i \"s/#{regex}/#{with}/\" #{options[:in]}", check_string(with, options[:in]))
92
+ end
93
+
94
+ # (Over)write a file with the specified content
95
+ # @param [String] text Text to add
96
+ # @param [Hash] options
97
+ # @option options [String] :to File to write to
98
+ # @option options [String] :name Give the buffer a displayable name (e.g. when generated from a template)
99
+ def write text, options
100
+ Upload.new(NamedBuffer.new(options[:name], text), options[:to], check_string(text, options[:to]))
101
+ end
102
+ end
103
+ end
104
+
@@ -0,0 +1,30 @@
1
+ module Machines
2
+ class Help
3
+ def initialize
4
+ @actions = {
5
+ 'htpasswd' => 'Generates basic auth in webserver/conf/htpasswd',
6
+ 'new <DIR>' => 'Generates an example machines project in DIR',
7
+ 'dryrun <machine>' => 'Logs commands but does not run them',
8
+ 'tasks' => 'Lists the available tasks',
9
+ 'build <machine> [task]' => 'Builds your chosen machine. Optionally, build just one task',
10
+ 'list' => 'Lists the available machines',
11
+ 'packages' => 'Lists the available packages',
12
+ 'override <PACKAGE>' => 'Copies the default package into project/packages so it can be edited/overidden'
13
+ }
14
+ end
15
+
16
+ def actions
17
+ @actions.keys.map{|key| key.gsub(/ .*/, '')}
18
+ end
19
+
20
+ def syntax
21
+ <<HELP
22
+ machines v#{Machines::VERSION} - Ubuntu/Ruby configuration tool.
23
+ machines COMMAND
24
+ COMMAND can be:
25
+ #{@actions.map{|action, help| " %-25s#{help}" % action}.join("\n")}
26
+ HELP
27
+ end
28
+ end
29
+ end
30
+