mofa 0.2.17 → 0.3.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.
@@ -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