/
cli.rb
143 lines (119 loc) · 5.12 KB
/
cli.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
require 'open3'
require 'ostruct'
module Sunzi
class Cli < Thor
include Thor::Actions
desc 'create', 'Create sunzi project'
def create(project = 'sunzi')
do_create(project)
end
desc 'deploy [user@host:port] [role] [--sudo]', 'Deploy sunzi project'
method_options :sudo => false
def deploy(target, role = nil)
do_deploy(target, role, options.sudo?)
end
desc 'compile', 'Compile sunzi project'
def compile(role = nil)
do_compile(role)
end
desc 'setup [linode|digital_ocean]', 'Setup a new VM'
def setup(provider)
Sunzi::Cloud.new(self, provider).setup
end
desc 'teardown [linode|digital_ocean] [name]', 'Teardown an existing VM'
def teardown(provider, name)
Sunzi::Cloud.new(self, provider).teardown(name)
end
desc 'version', 'Show version'
def version
puts Gem.loaded_specs['sunzi'].version.to_s
end
no_tasks do
include Sunzi::Utility
def self.source_root
File.expand_path('../../',__FILE__)
end
def do_create(project)
copy_file 'templates/create/.gitignore', "#{project}/.gitignore"
copy_file 'templates/create/sunzi.yml', "#{project}/sunzi.yml"
copy_file 'templates/create/install.sh', "#{project}/install.sh"
copy_file 'templates/create/recipes/sunzi.sh', "#{project}/recipes/sunzi.sh"
copy_file 'templates/create/roles/db.sh', "#{project}/roles/db.sh"
copy_file 'templates/create/roles/web.sh', "#{project}/roles/web.sh"
copy_file 'templates/create/files/.gitkeep', "#{project}/files/.gitkeep"
end
def do_deploy(target, role, force_sudo)
sudo = 'sudo ' if force_sudo
user, host, port = parse_target(target)
endpoint = "#{user}@#{host}"
# compile attributes and recipes
do_compile(role)
# The host key might change when we instantiate a new VM, so
# we remove (-R) the old host key from known_hosts.
`ssh-keygen -R #{host} 2> /dev/null`
remote_commands = <<-EOS
rm -rf ~/sunzi &&
mkdir ~/sunzi &&
cd ~/sunzi &&
tar xz &&
#{sudo}bash install.sh
EOS
remote_commands.strip! << ' && rm -rf ~/sunzi' if @config['preferences'] and @config['preferences']['erase_remote_folder']
local_commands = <<-EOS
cd compiled
tar cz . | ssh -o 'StrictHostKeyChecking no' #{endpoint} -p #{port} '#{remote_commands}'
EOS
Open3.popen3(local_commands) do |stdin, stdout, stderr|
stdin.close
t = Thread.new do
while (line = stderr.gets)
print line.color(:red)
end
end
while (line = stdout.gets)
print line.color(:green)
end
t.join
end
end
def do_compile(role)
# Check if you're in the sunzi directory
abort_with 'You must be in the sunzi folder' unless File.exists?('sunzi.yml')
# Check if role exists
abort_with "#{role} doesn't exist!" if role and !File.exists?("roles/#{role}.sh")
# Load sunzi.yml
@config = YAML.load(File.read('sunzi.yml'))
# Break down attributes into individual files
(@config['attributes'] || {}).each {|key, value| create_file "compiled/attributes/#{key}", value }
# Retrieve remote recipes via HTTP
cache_remote_recipes = @config['preferences'] && @config['preferences']['cache_remote_recipes']
(@config['recipes'] || []).each do |key, value|
next if cache_remote_recipes and File.exists?("compiled/recipes/#{key}.sh")
get value, "compiled/recipes/#{key}.sh"
end
# Copy local files
@attributes = OpenStruct.new(@config['attributes'])
copy_or_template = (@config['preferences'] && @config['preferences']['eval_erb']) ? :template : :copy_file
Dir['recipes/*'].each {|file| send copy_or_template, File.expand_path(file), "compiled/recipes/#{File.basename(file)}" }
Dir['roles/*'].each {|file| send copy_or_template, File.expand_path(file), "compiled/roles/#{File.basename(file)}" }
Dir['files/*'].each {|file| send copy_or_template, File.expand_path(file), "compiled/files/#{File.basename(file)}" }
(@config['files'] || []).each {|file| send copy_or_template, File.expand_path(file), "compiled/files/#{File.basename(file)}" }
# Build install.sh
if role
if copy_or_template == :template
template File.expand_path('install.sh'), 'compiled/_install.sh'
create_file 'compiled/install.sh', File.binread('compiled/_install.sh') << "\n" << File.binread("compiled/roles/#{role}.sh")
else
create_file 'compiled/install.sh', File.binread('install.sh') << "\n" << File.binread("roles/#{role}.sh")
end
else
send copy_or_template, File.expand_path('install.sh'), 'compiled/install.sh'
end
end
def parse_target(target)
target.match(/(.*@)?(.*?)(:.*)?$/)
[ ($1 && $1.delete('@') || 'root'), $2, ($3 && $3.delete(':') || '22') ]
end
end
end
end