machines 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+