machines 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +11 -0
- data/EXAMPLES.md +18 -0
- data/Gemfile +4 -0
- data/Guardfile +14 -0
- data/INSTALL.md +25 -0
- data/LICENSE +23 -0
- data/README.md +271 -0
- data/Rakefile +60 -0
- data/TODO.md +92 -0
- data/bin/machines +6 -0
- data/lib/machines/app_settings.rb +54 -0
- data/lib/machines/base.rb +13 -0
- data/lib/machines/checks.rb +63 -0
- data/lib/machines/cloud_machine.rb +33 -0
- data/lib/machines/command.rb +86 -0
- data/lib/machines/commandline.rb +148 -0
- data/lib/machines/configuration.rb +49 -0
- data/lib/machines/core.rb +117 -0
- data/lib/machines/database.rb +17 -0
- data/lib/machines/file_operations.rb +104 -0
- data/lib/machines/help.rb +30 -0
- data/lib/machines/installation.rb +151 -0
- data/lib/machines/log_command.rb +22 -0
- data/lib/machines/logger.rb +65 -0
- data/lib/machines/machinesfile.rb +25 -0
- data/lib/machines/named_buffer.rb +9 -0
- data/lib/machines/questions.rb +15 -0
- data/lib/machines/services.rb +24 -0
- data/lib/machines/upload.rb +29 -0
- data/lib/machines/version.rb +4 -0
- data/lib/machines.rb +19 -0
- data/lib/packages/abiword.rb +11 -0
- data/lib/packages/amazon_mp3.rb +4 -0
- data/lib/packages/awstats.rb +16 -0
- data/lib/packages/base.rb +14 -0
- data/lib/packages/chrome.rb +12 -0
- data/lib/packages/cruisecontrol.rb +22 -0
- data/lib/packages/dependencies.rb +10 -0
- data/lib/packages/docky.rb +36 -0
- data/lib/packages/dotfiles.rb +26 -0
- data/lib/packages/file_roller.rb +12 -0
- data/lib/packages/finalise.rb +4 -0
- data/lib/packages/firefox.rb +4 -0
- data/lib/packages/gedit.rb +11 -0
- data/lib/packages/git.rb +4 -0
- data/lib/packages/gmate.rb +33 -0
- data/lib/packages/gnome.rb +10 -0
- data/lib/packages/gnumeric.rb +11 -0
- data/lib/packages/hosts.rb +13 -0
- data/lib/packages/load_machines.rb +38 -0
- data/lib/packages/monit.rb +10 -0
- data/lib/packages/mysql.rb +46 -0
- data/lib/packages/nginx.rb +22 -0
- data/lib/packages/nginx_logrotate.rb +26 -0
- data/lib/packages/openbox.rb +35 -0
- data/lib/packages/passenger.rb +14 -0
- data/lib/packages/passenger_nginx.rb +8 -0
- data/lib/packages/postfix.rb +10 -0
- data/lib/packages/questions.rb +5 -0
- data/lib/packages/rbenv.rb +27 -0
- data/lib/packages/rvm.rb +20 -0
- data/lib/packages/save_machines.rb +4 -0
- data/lib/packages/slim.rb +6 -0
- data/lib/packages/sqlserver.rb +5 -0
- data/lib/packages/subtle.rb +29 -0
- data/lib/packages/sudo_mods.rb +6 -0
- data/lib/packages/time.rb +6 -0
- data/lib/packages/time_daily.rb +5 -0
- data/lib/packages/timezone.rb +10 -0
- data/lib/packages/unison.rb +5 -0
- data/lib/packages/virtualbox.rb +11 -0
- data/lib/packages/virtualbox_guest.rb +7 -0
- data/lib/packages/webapps.rb +36 -0
- data/lib/template/Machinesfile +48 -0
- data/lib/template/certificates/example.com.crt +0 -0
- data/lib/template/certificates/example.com.key +0 -0
- data/lib/template/certificates/selfsigned.crt +14 -0
- data/lib/template/certificates/selfsigned.key +16 -0
- data/lib/template/config.yml +98 -0
- data/lib/template/logrotate/app.erb +10 -0
- data/lib/template/logrotate/nginx.erb +12 -0
- data/lib/template/machines.yml +179 -0
- data/lib/template/misc/awstats.conf.erb +7 -0
- data/lib/template/misc/ntp.conf +7 -0
- data/lib/template/monit/conf.d/delayed_job.erb +11 -0
- data/lib/template/monit/conf.d/mysql.erb +7 -0
- data/lib/template/monit/conf.d/nginx +5 -0
- data/lib/template/monit/conf.d/postfix +7 -0
- data/lib/template/monit/conf.d/ssh +6 -0
- data/lib/template/monit/conf.d/system.erb +14 -0
- data/lib/template/monit/monitrc.erb +10 -0
- data/lib/template/monit/upstart.conf +16 -0
- data/lib/template/mysql/dbmaster.cnf +7 -0
- data/lib/template/mysql/dbslave.cnf +3 -0
- data/lib/template/nginx/app_server.conf.erb +87 -0
- data/lib/template/nginx/nginx.conf.erb +46 -0
- data/lib/template/nginx/upstart.conf.erb +21 -0
- data/lib/template/packages/custom.rb +17 -0
- data/lib/template/packages/productivity.rb +18 -0
- data/lib/template/slim/themes/dark/background.jpg +0 -0
- data/lib/template/slim/themes/dark/panel.png +0 -0
- data/lib/template/slim/themes/dark/slim.theme +39 -0
- data/lib/template/users/phil/dotfiles/bash_aliases +45 -0
- data/lib/template/users/phil/dotfiles/config/Trolltech.conf +4 -0
- data/lib/template/users/phil/dotfiles/config/gtk-3.0/settings.ini +9 -0
- data/lib/template/users/phil/dotfiles/config/openbox/autostart.sh +14 -0
- data/lib/template/users/phil/dotfiles/config/openbox/rc.xml +482 -0
- data/lib/template/users/phil/dotfiles/config/terminator/config +10 -0
- data/lib/template/users/phil/dotfiles/fonts.conf +15 -0
- data/lib/template/users/phil/dotfiles/gitconfig +27 -0
- data/lib/template/users/phil/dotfiles/gtkrc-2.0 +16 -0
- data/lib/template/users/phil/dotfiles/local/share/applications/mimeapps.list +4 -0
- data/lib/template/users/phil/dotfiles/unison/default.prf +33 -0
- data/lib/template/users/www/authorized_keys +0 -0
- data/lib/template/users/www/dotfiles/bash_aliases +40 -0
- data/lib/template/webapps.yml +75 -0
- data/machines.gemspec +44 -0
- data/spec/acceptance/dev_machine_spec.rb +22 -0
- data/spec/lib/machines/app_settings_spec.rb +106 -0
- data/spec/lib/machines/checks_spec.rb +105 -0
- data/spec/lib/machines/cloud_machine_spec.rb +36 -0
- data/spec/lib/machines/command_spec.rb +184 -0
- data/spec/lib/machines/commandline_spec.rb +299 -0
- data/spec/lib/machines/configuration_spec.rb +61 -0
- data/spec/lib/machines/core_spec.rb +299 -0
- data/spec/lib/machines/database_spec.rb +51 -0
- data/spec/lib/machines/file_operations_spec.rb +124 -0
- data/spec/lib/machines/help_spec.rb +22 -0
- data/spec/lib/machines/installation_spec.rb +176 -0
- data/spec/lib/machines/log_command_spec.rb +16 -0
- data/spec/lib/machines/logger_spec.rb +70 -0
- data/spec/lib/machines/machinesfile_spec.rb +34 -0
- data/spec/lib/machines/questions_spec.rb +73 -0
- data/spec/lib/machines/services_spec.rb +26 -0
- data/spec/lib/machines/upload_spec.rb +86 -0
- data/spec/lib/packages/abiword_spec.rb +20 -0
- data/spec/lib/packages/amazon_mp3_spec.rb +17 -0
- data/spec/lib/packages/awstats_spec.rb +26 -0
- data/spec/lib/packages/base_spec.rb +21 -0
- data/spec/lib/packages/chrome_spec.rb +30 -0
- data/spec/lib/packages/cruisecontrol_spec.rb +33 -0
- data/spec/lib/packages/dependencies_spec.rb +20 -0
- data/spec/lib/packages/docky_spec.rb +32 -0
- data/spec/lib/packages/dotfiles_spec.rb +44 -0
- data/spec/lib/packages/file_roller_spec.rb +69 -0
- data/spec/lib/packages/firefox_spec.rb +16 -0
- data/spec/lib/packages/gedit_spec.rb +20 -0
- data/spec/lib/packages/git_spec.rb +16 -0
- data/spec/lib/packages/gmate_spec.rb +39 -0
- data/spec/lib/packages/gnome_spec.rb +22 -0
- data/spec/lib/packages/gnumeric_spec.rb +21 -0
- data/spec/lib/packages/hosts_spec.rb +41 -0
- data/spec/lib/packages/load_machines_spec.rb +118 -0
- data/spec/lib/packages/monit_spec.rb +34 -0
- data/spec/lib/packages/mysql_spec.rb +69 -0
- data/spec/lib/packages/nginx_logrotate_spec.rb +80 -0
- data/spec/lib/packages/nginx_spec.rb +46 -0
- data/spec/lib/packages/openbox_spec.rb +41 -0
- data/spec/lib/packages/passenger_nginx_spec.rb +20 -0
- data/spec/lib/packages/passenger_spec.rb +26 -0
- data/spec/lib/packages/postfix_spec.rb +19 -0
- data/spec/lib/packages/questions_spec.rb +29 -0
- data/spec/lib/packages/rbenv_spec.rb +32 -0
- data/spec/lib/packages/rvm_spec.rb +31 -0
- data/spec/lib/packages/save_machines_spec.rb +51 -0
- data/spec/lib/packages/slim_spec.rb +22 -0
- data/spec/lib/packages/sqlserver_spec.rb +17 -0
- data/spec/lib/packages/timezone_spec.rb +27 -0
- data/spec/lib/packages/unison_spec.rb +17 -0
- data/spec/lib/packages/virtualbox_guest_spec.rb +25 -0
- data/spec/lib/packages/virtualbox_spec.rb +23 -0
- data/spec/lib/packages/webapps_spec.rb +70 -0
- data/spec/spec_helper.rb +103 -0
- data/spec/support/coverage.rb +8 -0
- data/spec/support/fake_out.rb +22 -0
- data/spec/support/fakefs_additions.rb +10 -0
- data/spec/support/minitest.rb +69 -0
- data/spec/support/vm_control.rb +54 -0
- data/tmp/.gitkeep +0 -0
- 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
|
+
|