mofa 0.2.17 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -44,6 +44,11 @@ class AttributesMap
44
44
  attr_hash.each do |key, value|
45
45
  if value.is_a?(Hash)
46
46
  new_attr_hash[key] = deep_parse(value, placeholder, content)
47
+ elsif value.is_a?(Array)
48
+ new_attr_hash[key] = []
49
+ value.each do |value_item|
50
+ new_attr_hash[key].push(value_item.gsub(Regexp.new(Regexp.escape(placeholder)), content))
51
+ end
47
52
  else
48
53
  new_attr_hash[key] = value.gsub(Regexp.new(Regexp.escape(placeholder)), content)
49
54
  end
@@ -72,4 +77,4 @@ class AttributesMap
72
77
  new_attr_hash
73
78
  end
74
79
 
75
- end
80
+ end
data/lib/mofa/cookbook.rb CHANGED
@@ -25,7 +25,6 @@ class Cookbook
25
25
  case
26
26
  when cookbook_name_or_path.match(/@/)
27
27
  fail "Did not find released Cookbook #{cookbook_name_or_path}!" unless ReleasedCookbook.exists?(cookbook_name_or_path)
28
- fail "Did not find Version #{cookbook_version} of released Cookbook #{cookbook_name_or_path}!" unless ReleasedCookbook.exists?(cookbook_name_or_path, cookbook_version)
29
28
 
30
29
  cookbook = ReleasedCookbook.new(cookbook_name_or_path)
31
30
 
@@ -33,7 +32,7 @@ class Cookbook
33
32
  cookbook = SourceCookbook.new(cookbook_name_or_path)
34
33
  end
35
34
  rescue RuntimeError => e
36
- error e.message
35
+ say e.message
37
36
  raise "Cookbook not found/detected!"
38
37
  end
39
38
  cookbook.token = token
@@ -42,8 +42,93 @@ class ProvisionCmd < MofaCmd
42
42
  # This Code is Copy'n'Pasted from the old mofa tooling. Only to make the MVP work in time!!
43
43
  # This needs to be refactored ASAP.
44
44
 
45
+ def prepare_host(hostname, host_index, solo_dir)
46
+ prerequesits_met = false
47
+ puts
48
+ puts "----------------------------------------------------------------------"
49
+ puts "Chef-Solo on Host #{hostname} (#{host_index}/#{hostlist.list.length.to_s})"
50
+ puts "----------------------------------------------------------------------"
51
+
52
+ puts "Pinging host #{hostname}..."
53
+ exit_status = system("ping -q -c 1 #{hostname} >/dev/null 2>&1")
54
+ unless exit_status then
55
+ puts " --> Host #{hostname} is unavailable!"
56
+ chef_solo_runs[hostname].store('status', 'UNAVAIL')
57
+ chef_solo_runs[hostname].store('status_msg', "Host #{hostname} unreachable.")
58
+ else
59
+ puts " --> Host #{hostname} is available."
60
+ prerequesits_met = true
61
+ 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|
62
+ puts "Remotely creating solo_dir \"#{solo_dir}\" on host #{hostname}"
63
+ # remotely create the temp folder
64
+ out = ssh_exec!(ssh, "[ -d #{solo_dir} ] || mkdir #{solo_dir}")
65
+ puts "ERROR (#{out[0]}): #{out[2]}" if out[0] != 0
66
+
67
+ # remotely create a data_bags folder structure on the target host
68
+ if cookbook.instance_of?(SourceCookbook) and File.directory?("#{cookbook.source_dir}/data_bags")
69
+ Dir.entries("#{cookbook.source_dir}/data_bags").select { |f| !f.match(/^\.\.?$/) }.each do |data_bag|
70
+ puts "Remotely creating data_bags dir \"#{solo_dir}/data_bags/#{data_bag}\""
71
+ out = ssh_exec!(ssh, "[ -d #{solo_dir}/data_bags/#{data_bag} ] || mkdir -p #{solo_dir}/data_bags/#{data_bag}")
72
+ puts "ERROR (#{out[0]}): #{out[2]}" if out[0] != 0
73
+ end
74
+ end
75
+ end
76
+ end
77
+ prerequesits_met
78
+ end
79
+
80
+ def create_solo_rb(sftp, hostname, solo_dir)
81
+ puts "Remotely creating \"#{solo_dir}/solo.rb\""
82
+ sftp.file.open("#{solo_dir}/solo.rb", "w") do |file|
83
+ if cookbook.instance_of?(SourceCookbook)
84
+ solo_rb = <<-"EOF"
85
+ cookbook_path [ "#{solo_dir}/cookbooks" ]
86
+ EOF
87
+ else
88
+ solo_rb = <<-"EOF"
89
+ recipe_url "#{cookbook.cookbooks_url}"
90
+ EOF
91
+ end
92
+ solo_rb += <<-"EOF"
93
+ data_bag_path "#{solo_dir}/data_bags"
94
+ log_level :info
95
+ log_location "#{solo_dir}/log"
96
+ verify_api_cert true
97
+ EOF
98
+
99
+ file.write(solo_rb)
100
+ end
101
+ end
102
+
103
+ def create_node_json(sftp, hostname, solo_dir, attributes_map)
104
+ puts "Remotely creating \"#{solo_dir}/node.json\""
105
+ node_json = {}
106
+ node_json.store('run_list', runlist_map.mp[hostname])
107
+ attributes_map.mp[hostname].each do |key, value|
108
+ node_json.store(key, value)
109
+ end
110
+
111
+ sftp.file.open("#{solo_dir}/node.json", "w") do |file|
112
+ file.write(JSON.pretty_generate(node_json))
113
+ end
114
+ end
115
+
116
+ def create_data_bags(sftp, hostname, solo_dir)
117
+ if cookbook.instance_of?(SourceCookbook) and File.directory?("#{cookbook.source_dir}/data_bags")
118
+ Dir.entries("#{cookbook.source_dir}/data_bags").select { |f| File.directory?(f) && !f.match(/^\.\.?$/) }.each do |data_bag|
119
+ Dir.entries("#{cookbook.source_dir}/data_bags/#{data_bag}").select { |f| f.match(/\.json$/) }.each do |data_bag_item|
120
+ puts "Uploading data_bag_item #{data_bag_item}... "
121
+ sftp.upload!("#{cookbook.source_dir}/data_bags/#{data_bag}/#{data_bag_item}", "#{solo_dir}/data_bags/#{data_bag}/#{data_bag_item}")
122
+ puts "OK."
123
+ end
124
+ end
125
+ end
126
+ end
127
+
45
128
  def run_chef_solo_on_hosts
46
129
  time = Time.new
130
+ # Create a temp working dir on the target host
131
+ solo_dir = '/var/tmp/' + time.strftime('%Y-%m-%d_%H%M%S')
47
132
  puts
48
133
  puts 'Chef-Solo Run started at ' + time.strftime('%Y-%m-%d %H:%M:%S')
49
134
  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']}"
@@ -52,82 +137,19 @@ class ProvisionCmd < MofaCmd
52
137
  host_index = 0
53
138
  hostlist.list.each do |hostname|
54
139
  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 "----------------------------------------------------------------------"
140
+ next unless prepare_host(hostname, host_index, solo_dir)
59
141
  chef_solo_runs.store(hostname, {})
60
142
 
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
143
  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
144
 
95
145
  # 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
146
+ create_solo_rb(sftp, hostname, solo_dir)
108
147
 
109
148
  # 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
149
+ create_node_json(sftp, hostname, solo_dir, attributes_map)
120
150
 
121
151
  # remotely create data_bag items
122
- if File.directory?("#{cookbook.source_dir}/data_bags")
123
- Dir.entries("#{cookbook.source_dir}/data_bags").select { |f| File.directory?(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
152
+ create_data_bags(sftp, hostname, solo_dir)
131
153
 
132
154
  if cookbook.instance_of?(SourceCookbook)
133
155
  puts "Cookbook is a SourceCookbook! Uploading Snapshot Package #{cookbook.pkg_name}... "
@@ -1,10 +1,24 @@
1
1
  class ReleasedCookbook < Cookbook
2
2
 
3
+ def self.get_name_and_version(cookbook_name_or_path)
4
+ # TODO: this needs proper vaidation!
5
+ name = cookbook_name_or_path.split(/@/).first
6
+ version = cookbook_name_or_path.split(/@/).last
7
+ { 'name' => name, 'version' => version }
8
+ end
9
+
10
+ def self.exists?(cookbook_name_or_path)
11
+ nv = get_name_and_version(cookbook_name_or_path)
12
+ url = "#{Mofa::Config.config['bin_repo']}/#{nv['name']}/#{nv['version']}/#{nv['name']}_#{nv['version']}-full.tar.gz"
13
+ puts "Checking if cookbook exists: #{url}"
14
+ RestClient.head(url)
15
+ end
16
+
3
17
  def initialize(cookbook_name_or_path)
4
18
  super()
5
- # TODO: this needs proper vaidation!
6
- @name = cookbook_name_or_path.split(/@/).first
7
- @version = cookbook_name_or_path.split(/@/).last
19
+ nv = ReleasedCookbook.get_name_and_version(cookbook_name_or_path)
20
+ @name = nv['name']
21
+ @version = nv['version']
8
22
  end
9
23
 
10
24
  # ------------- Interface Methods
@@ -12,19 +26,26 @@ class ReleasedCookbook < Cookbook
12
26
  def prepare
13
27
  @pkg_name ||= "#{name}_#{version}-full.tar.gz"
14
28
  @pkg_dir = "#{Mofa::Config.config['tmp_dir']}/.mofa/#{token}"
29
+ set_cookbooks_url
15
30
  end
16
31
 
17
32
  def execute
18
- # TODO: Download & unpack released cookbook
19
- # Important for guessing role runlists (when cookbook is an env-cookbook)
20
33
  end
21
34
 
22
35
  def cleanup
23
36
  say "Removing folder #{pkg_dir}...#{nl}"
24
- run "rm -r #{pkg_dir}"
37
+ run "rm -rf #{pkg_dir}"
25
38
  ok
26
39
  end
27
40
 
41
+ def load_mofa_yml
42
+ @mofa_yml = MofaYml.load_from_file(".mofa.yml", self)
43
+ end
44
+
45
+ def load_mofa_yml_local
46
+ @mofa_yml_local = MofaYml.load_from_file(".mofa.local.yml", self)
47
+ end
48
+
28
49
  # ------------- /Interface Methods
29
50
 
30
51
  def cleanup!
@@ -36,6 +57,16 @@ class ReleasedCookbook < Cookbook
36
57
  end
37
58
  end
38
59
 
60
+ def set_cookbooks_url
61
+ say 'Using remote URI as cookbooks_url: '
62
+ @cookbooks_url = "#{Mofa::Config.config['bin_repo']}/#{@name}/#{@version}/#{@name}_#{@version}-full.tar.gz"
63
+ say "#{@cookbooks_url}"
64
+ end
65
+
66
+ def recipes
67
+ []
68
+ end
69
+
39
70
  private
40
71
 
41
72
  def nl
@@ -11,6 +11,7 @@ class RunlistMap
11
11
  rl.cookbook = cookbook
12
12
  rl.hostlist = hostlist
13
13
  rl.token = token
14
+ rl.option_runlist = option_runlist
14
15
  rl.default_runlist_recipes = (!option_runlist.nil?) ? option_runlist : nil
15
16
  rl
16
17
  end
@@ -23,11 +24,17 @@ class RunlistMap
23
24
  @default_runlist_recipes ||= [ "#{cookbook.name}::default" ]
24
25
  @default_runlist_recipes = [ "#{@default_runlist_recipes}" ] unless @default_runlist_recipes.kind_of?(Array)
25
26
 
26
- case cookbook.type
27
- when 'env'
28
- guess_runlists_by_hostnames
29
- else
30
- set_default_runlist_for_every_host
27
+ if option_runlist.nil?
28
+ case cookbook.type
29
+ when 'env'
30
+ guess_runlists_by_hostnames
31
+ else
32
+ set_default_runlist_for_every_host
33
+ end
34
+ else
35
+ hostlist.list.each do |hostname|
36
+ @mp.store(hostname, option_runlist)
37
+ end
31
38
  end
32
39
  end
33
40
 
@@ -50,9 +57,9 @@ class RunlistMap
50
57
  hostlist.list.each do |hostname|
51
58
  @default_runlist_recipes.each do |rl_entry|
52
59
  next unless rl_entry.split(/::/)[0] == cookbook.name
53
- @mp.store(hostname, rl_entry) if cookbook.recipes.include?(rl_entry.split(/::/)[1])
60
+ @mp.store(hostname, rl_entry) if cookbook.recipes.kind_of?(Array) and cookbook.recipes.include?(rl_entry.split(/::/)[1])
54
61
  end
55
62
  end
56
63
  end
57
64
 
58
- end
65
+ end
@@ -22,11 +22,11 @@ class SourceCookbook < Cookbook
22
22
 
23
23
  @pkg_name ||= "#{name}_#{version}-SNAPSHOT.tar.gz"
24
24
  @pkg_dir = "#{source_dir}/.mofa/#{token}"
25
+ set_cookbooks_url
25
26
  end
26
27
 
27
28
  def execute
28
29
  package
29
- set_cookbooks_url
30
30
  end
31
31
 
32
32
  def cleanup
data/lib/mofa/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Mofa
2
- VERSION = "0.2.17"
2
+ VERSION = "0.3.0"
3
3
  end
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.2.17
4
+ version: 0.3.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-10-09 00:00:00.000000000 Z
12
+ date: 2015-10-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec