mofa 0.1.7 → 0.2.0
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.
- data/lib/mofa.rb +2 -0
- data/lib/mofa/cli.rb +28 -4
- data/lib/mofa/cookbook.rb +12 -11
- data/lib/mofa/mofa_cmd.rb +9 -198
- data/lib/mofa/provision_cmd.rb +204 -0
- data/lib/mofa/released_cookbook.rb +3 -5
- data/lib/mofa/runlist_map.rb +7 -5
- data/lib/mofa/source_cookbook.rb +7 -7
- data/lib/mofa/upload_cmd.rb +58 -0
- data/lib/mofa/version.rb +1 -1
- metadata +4 -2
data/lib/mofa.rb
CHANGED
data/lib/mofa/cli.rb
CHANGED
@@ -35,11 +35,35 @@ module Mofa
|
|
35
35
|
runlist_map = RunlistMap.create(cookbook, hostlist, token, options[:runlist])
|
36
36
|
attributes_map = AttributesMap.create(cookbook, hostlist, token, options[:runlist], options[:attributes])
|
37
37
|
|
38
|
-
|
39
|
-
mofa_cmd.prepare
|
40
|
-
mofa_cmd.execute
|
41
|
-
mofa_cmd.cleanup
|
38
|
+
cmd = ProvisionCmd.new(token, cookbook)
|
42
39
|
|
40
|
+
cmd.hostlist = hostlist
|
41
|
+
cmd.runlist_map = runlist_map
|
42
|
+
cmd.attributes_map = attributes_map
|
43
|
+
|
44
|
+
cmd.prepare
|
45
|
+
cmd.execute
|
46
|
+
cmd.cleanup
|
47
|
+
end
|
48
|
+
|
49
|
+
desc 'upload <cookbook>', 'package & upload cookbook into binrepo'
|
50
|
+
method_option :binrepo_host, :type => :string
|
51
|
+
method_option :binrepo_ssh_user, :type => :string
|
52
|
+
method_option :binrepo_ssh_keyfile, :type => :string
|
53
|
+
|
54
|
+
def upload(cookbook_path)
|
55
|
+
set_verbosity
|
56
|
+
|
57
|
+
cookbook_path ||= '.'
|
58
|
+
|
59
|
+
token = MofaCmd.generate_token
|
60
|
+
cookbook = Cookbook.create(cookbook_path, token)
|
61
|
+
|
62
|
+
cmd = UploadCmd.new(token, cookbook)
|
63
|
+
|
64
|
+
cmd.prepare
|
65
|
+
cmd.execute
|
66
|
+
cmd.cleanup
|
43
67
|
end
|
44
68
|
|
45
69
|
desc 'config', 'prints out mofa config.'
|
data/lib/mofa/cookbook.rb
CHANGED
@@ -10,6 +10,8 @@ class Cookbook
|
|
10
10
|
attr_accessor :name
|
11
11
|
attr_accessor :version
|
12
12
|
attr_accessor :type
|
13
|
+
attr_accessor :pkg_name
|
14
|
+
attr_accessor :pkg_dir
|
13
15
|
attr_accessor :pkg_uri
|
14
16
|
attr_accessor :source_uri
|
15
17
|
attr_accessor :cookbooks_url
|
@@ -17,29 +19,28 @@ class Cookbook
|
|
17
19
|
attr_accessor :mofa_yml_local
|
18
20
|
attr_accessor :token
|
19
21
|
|
20
|
-
def self.create(cookbook_name_or_path
|
21
|
-
|
22
|
+
def self.create(cookbook_name_or_path, token)
|
23
|
+
cookbook = nil
|
22
24
|
begin
|
23
25
|
case
|
24
|
-
when cookbook_name_or_path.match(
|
26
|
+
when cookbook_name_or_path.match(/@/)
|
25
27
|
fail "Did not find released Cookbook #{cookbook_name_or_path}!" unless ReleasedCookbook.exists?(cookbook_name_or_path)
|
26
28
|
fail "Did not find Version #{cookbook_version} of released Cookbook #{cookbook_name_or_path}!" unless ReleasedCookbook.exists?(cookbook_name_or_path, cookbook_version)
|
27
29
|
|
28
|
-
|
30
|
+
cookbook = ReleasedCookbook.new(cookbook_name_or_path)
|
29
31
|
|
30
32
|
else
|
31
|
-
|
33
|
+
cookbook = SourceCookbook.new(cookbook_name_or_path)
|
32
34
|
end
|
33
35
|
rescue RuntimeError => e
|
34
36
|
error e.message
|
35
37
|
raise "Cookbook not found/detected!"
|
36
38
|
end
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
39
|
+
cookbook.token = token
|
40
|
+
cookbook.autodetect_type
|
41
|
+
cookbook.load_mofa_yml
|
42
|
+
cookbook.load_mofa_yml_local
|
43
|
+
cookbook
|
43
44
|
end
|
44
45
|
|
45
46
|
def autodetect_type
|
data/lib/mofa/mofa_cmd.rb
CHANGED
@@ -4,215 +4,29 @@ require 'net/sftp'
|
|
4
4
|
class MofaCmd
|
5
5
|
attr_accessor :token
|
6
6
|
attr_accessor :cookbook
|
7
|
-
attr_accessor :hostlist
|
8
|
-
attr_accessor :runlist_map
|
9
|
-
attr_accessor :attributes_map
|
10
7
|
|
11
8
|
def self.generate_token
|
12
9
|
Digest::SHA1.hexdigest([Time.now, rand].join)[0..10]
|
13
10
|
end
|
14
11
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
mofa_cmd.cookbook = cookbook
|
19
|
-
mofa_cmd.hostlist = hostlist
|
20
|
-
mofa_cmd.runlist_map = runlist_map
|
21
|
-
mofa_cmd.attributes_map = attributes_map
|
22
|
-
mofa_cmd
|
12
|
+
def initialize(token, cookbook)
|
13
|
+
@token = token
|
14
|
+
@cookbook = cookbook
|
23
15
|
end
|
24
16
|
|
17
|
+
# @abstract
|
25
18
|
def prepare
|
26
|
-
|
19
|
+
raise RuntimeError, "must be implemented"
|
27
20
|
end
|
28
21
|
|
22
|
+
# @abstract
|
29
23
|
def execute
|
30
|
-
|
31
|
-
hostlist.retrieve
|
32
|
-
runlist_map.generate
|
33
|
-
attributes_map.generate
|
34
|
-
|
35
|
-
puts "Runlist Map: #{runlist_map.mp.inspect}"
|
36
|
-
puts "Attributes Map: #{attributes_map.mp.inspect}"
|
37
|
-
puts "Hostlist before runlist filtering: #{hostlist.list.inspect}"
|
38
|
-
|
39
|
-
hostlist.filter_by_runlist_map(runlist_map)
|
40
|
-
|
41
|
-
puts "Hostlist after runlist filtering: #{hostlist.list.inspect}"
|
42
|
-
|
43
|
-
exit_code = run_chef_solo_on_hosts
|
44
|
-
|
45
|
-
exit_code
|
24
|
+
raise RuntimeError, "must be implemented"
|
46
25
|
end
|
47
26
|
|
27
|
+
# @abstract
|
48
28
|
def cleanup
|
49
|
-
|
50
|
-
end
|
51
|
-
|
52
|
-
# FIXME
|
53
|
-
# This Code is Copy'n'Pasted from the old mofa tooling. Only to make the MVP work in time!!
|
54
|
-
# This needs to be refactored ASAP.
|
55
|
-
|
56
|
-
def run_chef_solo_on_hosts
|
57
|
-
time = Time.new
|
58
|
-
puts
|
59
|
-
puts 'Chef-Solo Run started at ' + time.strftime('%Y-%m-%d %H:%M:%S')
|
60
|
-
puts "Will use ssh_user #{Mofa::Config.config['ssh_user']} and ssh_key_file #{Mofa::Config.config['ssh_keyfile']}"
|
61
|
-
at_least_one_chef_solo_run_failed = false
|
62
|
-
chef_solo_runs = {}
|
63
|
-
host_index = 0
|
64
|
-
hostlist.list.each do |hostname|
|
65
|
-
host_index = host_index + 1
|
66
|
-
puts
|
67
|
-
puts "----------------------------------------------------------------------"
|
68
|
-
puts "Chef-Solo on Host #{hostname} (#{host_index}/#{hostlist.list.length.to_s})"
|
69
|
-
puts "----------------------------------------------------------------------"
|
70
|
-
chef_solo_runs.store(hostname, {})
|
71
|
-
|
72
|
-
# do only one for faster dev-cycle...
|
73
|
-
#next unless hostname.match(/^dash/)
|
74
|
-
|
75
|
-
puts "Pinging host #{hostname}..."
|
76
|
-
exit_status = system("ping -q -c 1 #{hostname} >/dev/null 2>&1")
|
77
|
-
unless exit_status then
|
78
|
-
puts " --> Host #{hostname} is unavailable!"
|
79
|
-
chef_solo_runs[hostname].store('status', 'UNAVAIL')
|
80
|
-
chef_solo_runs[hostname].store('status_msg', "Host #{hostname} unreachable.")
|
81
|
-
else
|
82
|
-
puts " --> Host #{hostname} is available."
|
83
|
-
prerequesits_met = true
|
84
|
-
# Create a temp working dir on the target host
|
85
|
-
solo_dir = '/var/tmp/' + time.strftime('%Y-%m-%d_%H%M%S')
|
86
|
-
Net::SSH.start(hostname, Mofa::Config.config['ssh_user'], :keys => [Mofa::Config.config['ssh_keyfile']], :verbose => :error) do |ssh|
|
87
|
-
puts "Remotely creating solo_dir \"#{solo_dir}\" on host #{hostname}"
|
88
|
-
# remotely create the temp folder
|
89
|
-
out = ssh_exec!(ssh, "[ -d #{solo_dir} ] || mkdir #{solo_dir}")
|
90
|
-
puts "ERROR (#{out[0]}): #{out[2]}" if out[0] != 0
|
91
|
-
|
92
|
-
# remotely create a data_bags folder structure on the target host
|
93
|
-
if File.directory?("#{cookbook.source_dir}/data_bags")
|
94
|
-
Dir.entries("#{cookbook.source_dir}/data_bags").select { |f| !f.match(/^\.\.?$/) }.each do |data_bag|
|
95
|
-
puts "Remotely creating data_bags dir \"#{solo_dir}/data_bags/#{data_bag}\""
|
96
|
-
out = ssh_exec!(ssh, "[ -d #{solo_dir}/data_bags/#{data_bag} ] || mkdir -p #{solo_dir}/data_bags/#{data_bag}")
|
97
|
-
puts "ERROR (#{out[0]}): #{out[2]}" if out[0] != 0
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
# skip the rest if prerequesits are not met
|
104
|
-
next unless prerequesits_met
|
105
|
-
|
106
|
-
|
107
|
-
Net::SFTP.start(hostname, Mofa::Config.config['ssh_user'], :keys => [Mofa::Config.config['ssh_keyfile']], :verbose => :error) do |sftp|
|
108
|
-
|
109
|
-
# remotely creating solo.rb
|
110
|
-
puts "Remotely creating \"#{solo_dir}/solo.rb\""
|
111
|
-
sftp.file.open("#{solo_dir}/solo.rb", "w") do |file|
|
112
|
-
solo_rb = <<-"EOF"
|
113
|
-
cookbook_path [ "#{solo_dir}/cookbooks" ]
|
114
|
-
data_bag_path "#{solo_dir}/data_bags"
|
115
|
-
log_level :info
|
116
|
-
log_location "#{solo_dir}/log"
|
117
|
-
verify_api_cert true
|
118
|
-
EOF
|
119
|
-
|
120
|
-
file.write(solo_rb)
|
121
|
-
end
|
122
|
-
|
123
|
-
# remotely creating node.json
|
124
|
-
puts "Remotely creating \"#{solo_dir}/node.json\""
|
125
|
-
node_json = {}
|
126
|
-
node_json.store('run_list', runlist_map.mp[hostname])
|
127
|
-
attributes_map.mp[hostname].each do |key, value|
|
128
|
-
node_json.store(key, value)
|
129
|
-
end
|
130
|
-
|
131
|
-
sftp.file.open("#{solo_dir}/node.json", "w") do |file|
|
132
|
-
file.write(JSON.pretty_generate(node_json))
|
133
|
-
end
|
134
|
-
|
135
|
-
# remotely create data_bag items
|
136
|
-
if File.directory?("#{cookbook.source_dir}/data_bags")
|
137
|
-
Dir.entries("#{cookbook.source_dir}/data_bags").select { |f| !f.match(/^\.\.?$/) }.each do |data_bag|
|
138
|
-
Dir.entries("#{cookbook.source_dir}/data_bags/#{data_bag}").select { |f| f.match(/\.json$/) }.each do |data_bag_item|
|
139
|
-
puts "Uploading data_bag_item #{data_bag_item}... "
|
140
|
-
sftp.upload!("#{cookbook.source_dir}/data_bags/#{data_bag}/#{data_bag_item}", "#{solo_dir}/data_bags/#{data_bag}/#{data_bag_item}")
|
141
|
-
puts "OK."
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
if cookbook.instance_of?(SourceCookbook)
|
147
|
-
puts "Cookbook is a SourceCookbook! Uploading Snapshot Package #{cookbook.pkg_name}... "
|
148
|
-
sftp.upload!("#{cookbook.pkg_dir}/#{cookbook.pkg_name}", "#{solo_dir}/#{cookbook.pkg_name}")
|
149
|
-
puts "OK."
|
150
|
-
end
|
151
|
-
|
152
|
-
# Do it -> Execute the chef-solo run!
|
153
|
-
Net::SSH.start(hostname, Mofa::Config::config['ssh_user'], :keys => [Mofa::Config::config['ssh_keyfile']], :verbose => :error) do |ssh|
|
154
|
-
|
155
|
-
if cookbook.instance_of?(SourceCookbook)
|
156
|
-
puts "Remotely unpacking Snapshot Package #{cookbook.pkg_name}... "
|
157
|
-
out = ssh_exec!(ssh, "cd #{solo_dir}; tar xvfz #{cookbook.pkg_name}")
|
158
|
-
if out[0] != 0
|
159
|
-
puts "ERROR (#{out[0]}): #{out[2]}"
|
160
|
-
puts out[1]
|
161
|
-
else
|
162
|
-
puts "OK."
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
puts "Remotely running chef-solo -c #{solo_dir}/solo.rb -j #{solo_dir}/node.json"
|
167
|
-
out = ssh_exec!(ssh, "sudo chef-solo -c #{solo_dir}/solo.rb -j #{solo_dir}/node.json")
|
168
|
-
if out[0] != 0
|
169
|
-
puts "ERROR (#{out[0]}): #{out[2]}"
|
170
|
-
out = ssh_exec!(ssh, "sudo cat #{solo_dir}/log")
|
171
|
-
puts "ERROR (#{out[0]}): #{out[2]}" if out[0] != 0
|
172
|
-
puts out[1]
|
173
|
-
chef_solo_runs[hostname].store('status', 'FAIL')
|
174
|
-
chef_solo_runs[hostname].store('status_msg', out[1])
|
175
|
-
else
|
176
|
-
unless Mofa::CLI::option_debug
|
177
|
-
out = ssh_exec!(ssh, "sudo grep 'Chef Run' #{solo_dir}/log")
|
178
|
-
puts "ERROR (#{out[0]}): #{out[2]}" if out[0] != 0
|
179
|
-
puts "Done."
|
180
|
-
else
|
181
|
-
out = ssh_exec!(ssh, "sudo cat #{solo_dir}/log")
|
182
|
-
puts "ERROR (#{out[0]}): #{out[2]}" if out[0] != 0
|
183
|
-
puts out[1]
|
184
|
-
end
|
185
|
-
chef_solo_runs[hostname].store('status', 'SUCCESS')
|
186
|
-
chef_solo_runs[hostname].store('status_msg', '')
|
187
|
-
end
|
188
|
-
out = ssh_exec!(ssh, "sudo chown -R #{Mofa::Config.config['ssh_user']}.#{Mofa::Config.config['ssh_user']} #{solo_dir}")
|
189
|
-
puts "ERROR (#{out[0]}): #{out[2]}" if out[0] != 0
|
190
|
-
end
|
191
|
-
end
|
192
|
-
at_least_one_chef_solo_run_failed = true if chef_solo_runs[hostname]['status'] == 'FAIL'
|
193
|
-
end
|
194
|
-
|
195
|
-
# ------- print out report
|
196
|
-
puts
|
197
|
-
puts "----------------------------------------------------------------------"
|
198
|
-
puts "Chef-Solo Run REPORT"
|
199
|
-
puts "----------------------------------------------------------------------"
|
200
|
-
puts "Chef-Solo has been run on #{chef_solo_runs.keys.length.to_s} hosts."
|
201
|
-
|
202
|
-
chef_solo_runs.each do |hostname, content|
|
203
|
-
status_msg = ''
|
204
|
-
status_msg = "(#{content['status_msg']})" if content['status'] == 'FAIL'
|
205
|
-
puts "#{content['status']}: #{hostname} #{status_msg}"
|
206
|
-
end
|
207
|
-
|
208
|
-
exit_code = 0
|
209
|
-
if at_least_one_chef_solo_run_failed
|
210
|
-
exit_code = 1
|
211
|
-
end
|
212
|
-
|
213
|
-
puts "Exiting with exit code #{exit_code}."
|
214
|
-
exit_code
|
215
|
-
|
29
|
+
raise RuntimeError, "must be implemented"
|
216
30
|
end
|
217
31
|
|
218
32
|
def ssh_exec!(ssh, command)
|
@@ -246,8 +60,5 @@ class MofaCmd
|
|
246
60
|
[exit_code, stdout_data, stderr_data, exit_signal]
|
247
61
|
end
|
248
62
|
|
249
|
-
|
250
|
-
private
|
251
|
-
|
252
63
|
end
|
253
64
|
|
@@ -0,0 +1,204 @@
|
|
1
|
+
require 'net/ssh'
|
2
|
+
require 'net/sftp'
|
3
|
+
|
4
|
+
class ProvisionCmd < MofaCmd
|
5
|
+
attr_accessor :hostlist
|
6
|
+
attr_accessor :runlist_map
|
7
|
+
attr_accessor :attributes_map
|
8
|
+
|
9
|
+
def initialize(token, cookbook)
|
10
|
+
super(token, cookbook)
|
11
|
+
end
|
12
|
+
|
13
|
+
def prepare
|
14
|
+
cookbook.prepare
|
15
|
+
end
|
16
|
+
|
17
|
+
def execute
|
18
|
+
cookbook.execute
|
19
|
+
|
20
|
+
hostlist.retrieve
|
21
|
+
runlist_map.generate
|
22
|
+
attributes_map.generate
|
23
|
+
|
24
|
+
puts "Runlist Map: #{runlist_map.mp.inspect}"
|
25
|
+
puts "Attributes Map: #{attributes_map.mp.inspect}"
|
26
|
+
puts "Hostlist before runlist filtering: #{hostlist.list.inspect}"
|
27
|
+
|
28
|
+
hostlist.filter_by_runlist_map(runlist_map)
|
29
|
+
|
30
|
+
puts "Hostlist after runlist filtering: #{hostlist.list.inspect}"
|
31
|
+
|
32
|
+
exit_code = run_chef_solo_on_hosts
|
33
|
+
|
34
|
+
exit_code
|
35
|
+
end
|
36
|
+
|
37
|
+
def cleanup
|
38
|
+
cookbook.cleanup
|
39
|
+
end
|
40
|
+
|
41
|
+
# FIXME
|
42
|
+
# This Code is Copy'n'Pasted from the old mofa tooling. Only to make the MVP work in time!!
|
43
|
+
# This needs to be refactored ASAP.
|
44
|
+
|
45
|
+
def run_chef_solo_on_hosts
|
46
|
+
time = Time.new
|
47
|
+
puts
|
48
|
+
puts 'Chef-Solo Run started at ' + time.strftime('%Y-%m-%d %H:%M:%S')
|
49
|
+
puts "Will use ssh_user #{Mofa::Config.config['ssh_user']}, ssh_port #{Mofa::Config.config['ssh_port']} and ssh_key_file #{Mofa::Config.config['ssh_keyfile']}"
|
50
|
+
at_least_one_chef_solo_run_failed = false
|
51
|
+
chef_solo_runs = {}
|
52
|
+
host_index = 0
|
53
|
+
hostlist.list.each do |hostname|
|
54
|
+
host_index = host_index + 1
|
55
|
+
puts
|
56
|
+
puts "----------------------------------------------------------------------"
|
57
|
+
puts "Chef-Solo on Host #{hostname} (#{host_index}/#{hostlist.list.length.to_s})"
|
58
|
+
puts "----------------------------------------------------------------------"
|
59
|
+
chef_solo_runs.store(hostname, {})
|
60
|
+
|
61
|
+
puts "Pinging host #{hostname}..."
|
62
|
+
exit_status = system("ping -q -c 1 #{hostname} >/dev/null 2>&1")
|
63
|
+
unless exit_status then
|
64
|
+
puts " --> Host #{hostname} is unavailable!"
|
65
|
+
chef_solo_runs[hostname].store('status', 'UNAVAIL')
|
66
|
+
chef_solo_runs[hostname].store('status_msg', "Host #{hostname} unreachable.")
|
67
|
+
else
|
68
|
+
puts " --> Host #{hostname} is available."
|
69
|
+
prerequesits_met = true
|
70
|
+
# Create a temp working dir on the target host
|
71
|
+
solo_dir = '/var/tmp/' + time.strftime('%Y-%m-%d_%H%M%S')
|
72
|
+
Net::SSH.start(hostname, Mofa::Config.config['ssh_user'], :keys => [Mofa::Config.config['ssh_keyfile']], :port => Mofa::Config.config['ssh_port'], :verbose => :error) do |ssh|
|
73
|
+
puts "Remotely creating solo_dir \"#{solo_dir}\" on host #{hostname}"
|
74
|
+
# remotely create the temp folder
|
75
|
+
out = ssh_exec!(ssh, "[ -d #{solo_dir} ] || mkdir #{solo_dir}")
|
76
|
+
puts "ERROR (#{out[0]}): #{out[2]}" if out[0] != 0
|
77
|
+
|
78
|
+
# remotely create a data_bags folder structure on the target host
|
79
|
+
if File.directory?("#{cookbook.source_dir}/data_bags")
|
80
|
+
Dir.entries("#{cookbook.source_dir}/data_bags").select { |f| !f.match(/^\.\.?$/) }.each do |data_bag|
|
81
|
+
puts "Remotely creating data_bags dir \"#{solo_dir}/data_bags/#{data_bag}\""
|
82
|
+
out = ssh_exec!(ssh, "[ -d #{solo_dir}/data_bags/#{data_bag} ] || mkdir -p #{solo_dir}/data_bags/#{data_bag}")
|
83
|
+
puts "ERROR (#{out[0]}): #{out[2]}" if out[0] != 0
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# skip the rest if prerequesits are not met
|
90
|
+
next unless prerequesits_met
|
91
|
+
|
92
|
+
|
93
|
+
Net::SFTP.start(hostname, Mofa::Config.config['ssh_user'], :keys => [Mofa::Config.config['ssh_keyfile']], :port => Mofa::Config.config['ssh_port'], :verbose => :error) do |sftp|
|
94
|
+
|
95
|
+
# remotely creating solo.rb
|
96
|
+
puts "Remotely creating \"#{solo_dir}/solo.rb\""
|
97
|
+
sftp.file.open("#{solo_dir}/solo.rb", "w") do |file|
|
98
|
+
solo_rb = <<-"EOF"
|
99
|
+
cookbook_path [ "#{solo_dir}/cookbooks" ]
|
100
|
+
data_bag_path "#{solo_dir}/data_bags"
|
101
|
+
log_level :info
|
102
|
+
log_location "#{solo_dir}/log"
|
103
|
+
verify_api_cert true
|
104
|
+
EOF
|
105
|
+
|
106
|
+
file.write(solo_rb)
|
107
|
+
end
|
108
|
+
|
109
|
+
# remotely creating node.json
|
110
|
+
puts "Remotely creating \"#{solo_dir}/node.json\""
|
111
|
+
node_json = {}
|
112
|
+
node_json.store('run_list', runlist_map.mp[hostname])
|
113
|
+
attributes_map.mp[hostname].each do |key, value|
|
114
|
+
node_json.store(key, value)
|
115
|
+
end
|
116
|
+
|
117
|
+
sftp.file.open("#{solo_dir}/node.json", "w") do |file|
|
118
|
+
file.write(JSON.pretty_generate(node_json))
|
119
|
+
end
|
120
|
+
|
121
|
+
# remotely create data_bag items
|
122
|
+
if File.directory?("#{cookbook.source_dir}/data_bags")
|
123
|
+
Dir.entries("#{cookbook.source_dir}/data_bags").select { |f| !f.match(/^\.\.?$/) }.each do |data_bag|
|
124
|
+
Dir.entries("#{cookbook.source_dir}/data_bags/#{data_bag}").select { |f| f.match(/\.json$/) }.each do |data_bag_item|
|
125
|
+
puts "Uploading data_bag_item #{data_bag_item}... "
|
126
|
+
sftp.upload!("#{cookbook.source_dir}/data_bags/#{data_bag}/#{data_bag_item}", "#{solo_dir}/data_bags/#{data_bag}/#{data_bag_item}")
|
127
|
+
puts "OK."
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
if cookbook.instance_of?(SourceCookbook)
|
133
|
+
puts "Cookbook is a SourceCookbook! Uploading Snapshot Package #{cookbook.pkg_name}... "
|
134
|
+
sftp.upload!("#{cookbook.pkg_dir}/#{cookbook.pkg_name}", "#{solo_dir}/#{cookbook.pkg_name}")
|
135
|
+
puts "OK."
|
136
|
+
end
|
137
|
+
|
138
|
+
# Do it -> Execute the chef-solo run!
|
139
|
+
Net::SSH.start(hostname, Mofa::Config::config['ssh_user'], :keys => [Mofa::Config::config['ssh_keyfile']], :port => Mofa::Config.config['ssh_port'], :verbose => :error) do |ssh|
|
140
|
+
|
141
|
+
if cookbook.instance_of?(SourceCookbook)
|
142
|
+
puts "Remotely unpacking Snapshot Package #{cookbook.pkg_name}... "
|
143
|
+
out = ssh_exec!(ssh, "cd #{solo_dir}; tar xvfz #{cookbook.pkg_name}")
|
144
|
+
if out[0] != 0
|
145
|
+
puts "ERROR (#{out[0]}): #{out[2]}"
|
146
|
+
puts out[1]
|
147
|
+
else
|
148
|
+
puts "OK."
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
puts "Remotely running chef-solo -c #{solo_dir}/solo.rb -j #{solo_dir}/node.json"
|
153
|
+
out = ssh_exec!(ssh, "sudo chef-solo -c #{solo_dir}/solo.rb -j #{solo_dir}/node.json")
|
154
|
+
if out[0] != 0
|
155
|
+
puts "ERROR (#{out[0]}): #{out[2]}"
|
156
|
+
out = ssh_exec!(ssh, "sudo cat #{solo_dir}/log")
|
157
|
+
puts "ERROR (#{out[0]}): #{out[2]}" if out[0] != 0
|
158
|
+
puts out[1]
|
159
|
+
chef_solo_runs[hostname].store('status', 'FAIL')
|
160
|
+
chef_solo_runs[hostname].store('status_msg', out[1])
|
161
|
+
else
|
162
|
+
unless Mofa::CLI::option_debug
|
163
|
+
out = ssh_exec!(ssh, "sudo grep 'Chef Run' #{solo_dir}/log")
|
164
|
+
puts "ERROR (#{out[0]}): #{out[2]}" if out[0] != 0
|
165
|
+
puts "Done."
|
166
|
+
else
|
167
|
+
out = ssh_exec!(ssh, "sudo cat #{solo_dir}/log")
|
168
|
+
puts "ERROR (#{out[0]}): #{out[2]}" if out[0] != 0
|
169
|
+
puts out[1]
|
170
|
+
end
|
171
|
+
chef_solo_runs[hostname].store('status', 'SUCCESS')
|
172
|
+
chef_solo_runs[hostname].store('status_msg', '')
|
173
|
+
end
|
174
|
+
out = ssh_exec!(ssh, "sudo chown -R #{Mofa::Config.config['ssh_user']}.#{Mofa::Config.config['ssh_user']} #{solo_dir}")
|
175
|
+
puts "ERROR (#{out[0]}): #{out[2]}" if out[0] != 0
|
176
|
+
end
|
177
|
+
end
|
178
|
+
at_least_one_chef_solo_run_failed = true if chef_solo_runs[hostname]['status'] == 'FAIL'
|
179
|
+
end
|
180
|
+
|
181
|
+
# ------- print out report
|
182
|
+
puts
|
183
|
+
puts "----------------------------------------------------------------------"
|
184
|
+
puts "Chef-Solo Run REPORT"
|
185
|
+
puts "----------------------------------------------------------------------"
|
186
|
+
puts "Chef-Solo has been run on #{chef_solo_runs.keys.length.to_s} hosts."
|
187
|
+
|
188
|
+
chef_solo_runs.each do |hostname, content|
|
189
|
+
status_msg = ''
|
190
|
+
status_msg = "(#{content['status_msg']})" if content['status'] == 'FAIL'
|
191
|
+
puts "#{content['status']}: #{hostname} #{status_msg}"
|
192
|
+
end
|
193
|
+
|
194
|
+
exit_code = 0
|
195
|
+
if at_least_one_chef_solo_run_failed
|
196
|
+
exit_code = 1
|
197
|
+
end
|
198
|
+
|
199
|
+
puts "Exiting with exit code #{exit_code}."
|
200
|
+
exit_code
|
201
|
+
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
@@ -1,18 +1,16 @@
|
|
1
1
|
class ReleasedCookbook < Cookbook
|
2
|
-
attr_accessor :pkg_dir
|
3
|
-
attr_accessor :pkg_name
|
4
2
|
|
5
3
|
def initialize(cookbook_name_or_path)
|
6
4
|
super()
|
7
5
|
# TODO: this needs proper vaidation!
|
8
|
-
@name = cookbook_name_or_path.split(
|
9
|
-
@version = cookbook_name_or_path.split(
|
6
|
+
@name = cookbook_name_or_path.split(/@/).first
|
7
|
+
@version = cookbook_name_or_path.split(/@/).last
|
10
8
|
end
|
11
9
|
|
12
10
|
# ------------- Interface Methods
|
13
11
|
|
14
12
|
def prepare
|
15
|
-
@pkg_name
|
13
|
+
@pkg_name ||= "#{name}_#{version}-full.tar.gz"
|
16
14
|
@pkg_dir = "#{Mofa::Config.config['tmp_dir']}/.mofa/#{token}"
|
17
15
|
end
|
18
16
|
|
data/lib/mofa/runlist_map.rb
CHANGED
@@ -4,14 +4,14 @@ class RunlistMap
|
|
4
4
|
attr_accessor :hostlist
|
5
5
|
attr_accessor :token
|
6
6
|
attr_accessor :option_runlist
|
7
|
-
attr_accessor :
|
7
|
+
attr_accessor :default_runlist_recipes
|
8
8
|
|
9
9
|
def self.create(cookbook, hostlist, token, option_runlist = nil)
|
10
10
|
rl = RunlistMap.new
|
11
11
|
rl.cookbook = cookbook
|
12
12
|
rl.hostlist = hostlist
|
13
13
|
rl.token = token
|
14
|
-
rl.
|
14
|
+
rl.default_runlist_recipes = (!option_runlist.nil?) ? option_runlist : nil
|
15
15
|
rl
|
16
16
|
end
|
17
17
|
|
@@ -20,7 +20,7 @@ class RunlistMap
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def generate
|
23
|
-
@
|
23
|
+
@default_runlist_recipes ||= [ "#{cookbook.name}::default" ]
|
24
24
|
|
25
25
|
case cookbook.type
|
26
26
|
when 'env'
|
@@ -47,8 +47,10 @@ class RunlistMap
|
|
47
47
|
|
48
48
|
def set_default_runlist_for_every_host
|
49
49
|
hostlist.list.each do |hostname|
|
50
|
-
|
51
|
-
|
50
|
+
puts "Default Runlist Recipes: #{@default_runlist_recipes}"
|
51
|
+
@default_runlist_recipes.each do |rl_entry|
|
52
|
+
next unless rl_entry.split(/::/)[0] == cookbook.name
|
53
|
+
@mp.store(hostname, @default_runlist) if cookbook.recipes.include?(rl_entry.split(/::/)[1])
|
52
54
|
end
|
53
55
|
end
|
54
56
|
end
|
data/lib/mofa/source_cookbook.rb
CHANGED
@@ -1,7 +1,4 @@
|
|
1
1
|
class SourceCookbook < Cookbook
|
2
|
-
attr_accessor :pkg_dir
|
3
|
-
attr_accessor :pkg_name
|
4
|
-
|
5
2
|
COOKBOOK_IGNORE=%w(.mofa .idea .kitchen .vagrant .bundle test)
|
6
3
|
|
7
4
|
def initialize(cookbook_name_or_path)
|
@@ -12,6 +9,7 @@ class SourceCookbook < Cookbook
|
|
12
9
|
@source_uri = "file://#{path.realpath}"
|
13
10
|
|
14
11
|
say "source_dir=#{source_dir}"
|
12
|
+
|
15
13
|
autodetect_name
|
16
14
|
autodetect_version
|
17
15
|
end
|
@@ -21,13 +19,14 @@ class SourceCookbook < Cookbook
|
|
21
19
|
def prepare
|
22
20
|
fail "Source URI is not a file:// URI!" unless source_uri =~ /^file:\/\/.*/
|
23
21
|
fail "Folder #{source_dir} is not a Cookbook Folder!" unless cookbook_folder?(source_dir)
|
24
|
-
|
22
|
+
|
23
|
+
@pkg_name ||= "#{name}_#{version}-SNAPSHOT.tar.gz"
|
25
24
|
@pkg_dir = "#{source_dir}/.mofa/#{token}"
|
26
25
|
end
|
27
26
|
|
28
27
|
def execute
|
29
28
|
package
|
30
|
-
|
29
|
+
set_cookbooks_url
|
31
30
|
end
|
32
31
|
|
33
32
|
def cleanup
|
@@ -37,6 +36,7 @@ class SourceCookbook < Cookbook
|
|
37
36
|
end
|
38
37
|
|
39
38
|
# ------------- /Interface Methods
|
39
|
+
|
40
40
|
def source_dir
|
41
41
|
source_uri.gsub(/^file:\/\//, '')
|
42
42
|
end
|
@@ -75,7 +75,7 @@ class SourceCookbook < Cookbook
|
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
|
-
def
|
78
|
+
def set_cookbooks_url
|
79
79
|
if mofahub_available?
|
80
80
|
say 'Staging (uploading to mofa-hub) Cookbook Snapshot: '
|
81
81
|
@cookbooks_url = upload_to_mofahub
|
@@ -115,7 +115,7 @@ class SourceCookbook < Cookbook
|
|
115
115
|
end
|
116
116
|
|
117
117
|
def cleanup_and_repackage
|
118
|
-
say "Shrinking Cookbook
|
118
|
+
say "Shrinking Cookbook #{pkg_name}... "
|
119
119
|
|
120
120
|
tar_verbose = (Mofa::CLI::option_debug) ? 'v' : ''
|
121
121
|
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'net/ssh'
|
2
|
+
require 'net/sftp'
|
3
|
+
|
4
|
+
class UploadCmd < MofaCmd
|
5
|
+
|
6
|
+
def initialize(token, cookbook)
|
7
|
+
super(token, cookbook)
|
8
|
+
end
|
9
|
+
|
10
|
+
def prepare
|
11
|
+
fail unless binrepo_up?
|
12
|
+
|
13
|
+
# upload always means: package a release
|
14
|
+
cookbook.pkg_name = "#{cookbook.name}_#{cookbook.version}-full.tar.gz"
|
15
|
+
cookbook.prepare
|
16
|
+
end
|
17
|
+
|
18
|
+
def execute
|
19
|
+
cookbook.execute
|
20
|
+
upload_cookbook_pkg
|
21
|
+
end
|
22
|
+
|
23
|
+
def cleanup
|
24
|
+
cookbook.cleanup
|
25
|
+
end
|
26
|
+
|
27
|
+
def binrepo_up?
|
28
|
+
binrepo_up = true
|
29
|
+
|
30
|
+
exit_status = system("ping -q -c 1 #{Mofa::Config.config['binrepo_host']} >/dev/null 2>&1")
|
31
|
+
unless exit_status then
|
32
|
+
puts " --> Binrepo host #{Mofa::Config.config['binrepo_host']} is unavailable!"
|
33
|
+
binrepo_up = false
|
34
|
+
end
|
35
|
+
|
36
|
+
puts "Binrepo #{ Mofa::Config.config['binrepo_ssh_user']}@#{Mofa::Config.config['binrepo_host']}:#{Mofa::Config.config['binrepo_import_dir']} not present or not reachable!" unless binrepo_up
|
37
|
+
binrepo_up
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
def upload_cookbook_pkg
|
42
|
+
puts "Will use ssh_user #{Mofa::Config.config['binrepo_ssh_user']} and ssh_key_file #{Mofa::Config.config['binrepo_ssh_keyfile']}"
|
43
|
+
puts "Uploading cookbook pkg #{cookbook.pkg_name} to binrepo import folder #{Mofa::Config.config['binrepo_host']}:#{Mofa::Config.config['binrepo_import_dir']}..."
|
44
|
+
|
45
|
+
fail unless binrepo_up?
|
46
|
+
begin
|
47
|
+
Net::SFTP.start(Mofa::Config.config['binrepo_host'], Mofa::Config.config['binrepo_ssh_user'], :keys => [Mofa::Config.config['binrepo_ssh_keyfile']], :port => Mofa::Config.config['binrepo_ssh_port'], :verbose => :error) do |sftp|
|
48
|
+
sftp.upload!("#{cookbook.pkg_dir}/#{cookbook.pkg_name}", "#{Mofa::Config.config['binrepo_import_dir']}/#{cookbook.pkg_name}")
|
49
|
+
end
|
50
|
+
puts "OK."
|
51
|
+
rescue RuntimeError => e
|
52
|
+
puts "Error: #{e.message}"
|
53
|
+
raise "Failed to upload cookbook #{cookbook.name}!"
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
data/lib/mofa/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mofa
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-04-
|
12
|
+
date: 2015-04-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -228,9 +228,11 @@ files:
|
|
228
228
|
- lib/mofa/hostlist.rb
|
229
229
|
- lib/mofa/mofa_cmd.rb
|
230
230
|
- lib/mofa/mofa_yml.rb
|
231
|
+
- lib/mofa/provision_cmd.rb
|
231
232
|
- lib/mofa/released_cookbook.rb
|
232
233
|
- lib/mofa/runlist_map.rb
|
233
234
|
- lib/mofa/source_cookbook.rb
|
235
|
+
- lib/mofa/upload_cmd.rb
|
234
236
|
- lib/mofa/version.rb
|
235
237
|
- mofa.gemspec
|
236
238
|
homepage: https://github.com/pingworks/mofa
|