knife-sharp 0.4.5 → 0.5.1
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.
- checksums.yaml +4 -4
- data/.gitignore +4 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +92 -0
- data/Rakefile +35 -0
- data/knife-sharp.gemspec +20 -0
- data/lib/chef/knife/sharp-align.rb +26 -465
- data/lib/chef/knife/sharp-backup.rb +8 -32
- data/lib/chef/knife/sharp-cookbook-align.rb +149 -0
- data/lib/chef/knife/sharp-data_bag-align.rb +141 -0
- data/lib/chef/knife/sharp-history.rb +6 -34
- data/lib/chef/knife/sharp-role-align.rb +134 -0
- data/lib/chef/knife/sharp-rollback.rb +7 -30
- data/lib/knife-sharp.rb +1 -1
- data/lib/knife-sharp/common.rb +113 -0
- metadata +16 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ca5eb5868e371ab216bf8009f49fc1341b0884dd
|
4
|
+
data.tar.gz: 4f2471fc8a99e9372dd459c9c0da29e7e5d9f862
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bdf6f7d68cfc0806231fedec65d4e074ea97bddbef433ee8f58ec9f180200c7337f8accf8e530919275a3d12f0f8dbb13672756faa8c482aeff8b82373f74be2
|
7
|
+
data.tar.gz: 732dcc82d02814c8d7e4fba9d00b1aaa2dd2037fae214cbb5e36d1e91769369ecdad99ac512adbc96b005913bf9f9aa954b50b90cc805a9382512e282637104b
|
data/.gitignore
ADDED
data/Gemfile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
gemspec
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
knife-sharp (0.5.0)
|
5
|
+
chef (>= 11)
|
6
|
+
grit (~> 2.5)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
specs:
|
10
|
+
chef (12.0.3)
|
11
|
+
chef-zero (~> 3.2)
|
12
|
+
diff-lcs (~> 1.2, >= 1.2.4)
|
13
|
+
erubis (~> 2.7)
|
14
|
+
ffi-yajl (~> 1.2)
|
15
|
+
highline (~> 1.6, >= 1.6.9)
|
16
|
+
mixlib-authentication (~> 1.3)
|
17
|
+
mixlib-cli (~> 1.4)
|
18
|
+
mixlib-config (~> 2.0)
|
19
|
+
mixlib-log (~> 1.3)
|
20
|
+
mixlib-shellout (>= 2.0.0.rc.0, < 3.0)
|
21
|
+
net-ssh (~> 2.6)
|
22
|
+
net-ssh-multi (~> 1.1)
|
23
|
+
ohai (~> 8.0)
|
24
|
+
plist (~> 3.1.0)
|
25
|
+
pry (~> 0.9)
|
26
|
+
chef-zero (3.2.1)
|
27
|
+
ffi-yajl (~> 1.1)
|
28
|
+
hashie (~> 2.0)
|
29
|
+
mixlib-log (~> 1.3)
|
30
|
+
rack
|
31
|
+
uuidtools (~> 2.1)
|
32
|
+
coderay (1.1.0)
|
33
|
+
diff-lcs (1.2.5)
|
34
|
+
erubis (2.7.0)
|
35
|
+
ffi (1.9.6)
|
36
|
+
ffi-yajl (1.3.1)
|
37
|
+
ffi (~> 1.5)
|
38
|
+
libyajl2 (~> 1.2)
|
39
|
+
grit (2.5.0)
|
40
|
+
diff-lcs (~> 1.1)
|
41
|
+
mime-types (~> 1.15)
|
42
|
+
posix-spawn (~> 0.3.6)
|
43
|
+
hashie (2.1.2)
|
44
|
+
highline (1.6.21)
|
45
|
+
ipaddress (0.8.0)
|
46
|
+
libyajl2 (1.2.0)
|
47
|
+
method_source (0.8.2)
|
48
|
+
mime-types (1.25.1)
|
49
|
+
mixlib-authentication (1.3.0)
|
50
|
+
mixlib-log
|
51
|
+
mixlib-cli (1.5.0)
|
52
|
+
mixlib-config (2.1.0)
|
53
|
+
mixlib-log (1.6.0)
|
54
|
+
mixlib-shellout (2.0.1)
|
55
|
+
net-dhcp (1.3.2)
|
56
|
+
net-ssh (2.9.2)
|
57
|
+
net-ssh-gateway (1.2.0)
|
58
|
+
net-ssh (>= 2.6.5)
|
59
|
+
net-ssh-multi (1.2.0)
|
60
|
+
net-ssh (>= 2.6.5)
|
61
|
+
net-ssh-gateway (>= 1.2.0)
|
62
|
+
ohai (8.0.1)
|
63
|
+
ffi (~> 1.9)
|
64
|
+
ffi-yajl (~> 1.1)
|
65
|
+
ipaddress
|
66
|
+
mime-types (~> 1.16)
|
67
|
+
mixlib-cli
|
68
|
+
mixlib-config (~> 2.0)
|
69
|
+
mixlib-log
|
70
|
+
mixlib-shellout (~> 2.0)
|
71
|
+
net-dhcp
|
72
|
+
rake (~> 10.1)
|
73
|
+
systemu (~> 2.6.4)
|
74
|
+
wmi-lite (~> 1.0)
|
75
|
+
plist (3.1.0)
|
76
|
+
posix-spawn (0.3.9)
|
77
|
+
pry (0.10.1)
|
78
|
+
coderay (~> 1.1.0)
|
79
|
+
method_source (~> 0.8.1)
|
80
|
+
slop (~> 3.4)
|
81
|
+
rack (1.6.0)
|
82
|
+
rake (10.1.0)
|
83
|
+
slop (3.6.0)
|
84
|
+
systemu (2.6.4)
|
85
|
+
uuidtools (2.1.5)
|
86
|
+
wmi-lite (1.0.0)
|
87
|
+
|
88
|
+
PLATFORMS
|
89
|
+
ruby
|
90
|
+
|
91
|
+
DEPENDENCIES
|
92
|
+
knife-sharp!
|
data/Rakefile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
|
5
|
+
def name
|
6
|
+
@name ||= Dir['*.gemspec'].first.split('.').first
|
7
|
+
end
|
8
|
+
|
9
|
+
def version
|
10
|
+
line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/]
|
11
|
+
line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
|
12
|
+
end
|
13
|
+
|
14
|
+
def gemspec_file
|
15
|
+
"#{name}.gemspec"
|
16
|
+
end
|
17
|
+
|
18
|
+
def gem_file
|
19
|
+
"#{name}-#{version}.gem"
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
# tasks
|
24
|
+
|
25
|
+
desc "Open an irb session preloaded with this library"
|
26
|
+
task :console do
|
27
|
+
sh "irb -rubygems -r ./lib/#{name}.rb"
|
28
|
+
end
|
29
|
+
|
30
|
+
desc "Build #{gem_file} into the pkg directory"
|
31
|
+
task :build do
|
32
|
+
sh "mkdir -p pkg"
|
33
|
+
sh "gem build #{gemspec_file}"
|
34
|
+
sh "mv #{gem_file} pkg"
|
35
|
+
end
|
data/knife-sharp.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'date'
|
2
|
+
require File.join(File.dirname(__FILE__), "lib", "knife-sharp.rb")
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'knife-sharp'
|
6
|
+
s.version = KnifeSharp::VERSION
|
7
|
+
s.date = Date.today.to_s
|
8
|
+
s.summary = "Knife sharp plugin"
|
9
|
+
s.description = "Sharpen your knife"
|
10
|
+
s.homepage = "https://github.com/Fotolia/knife-sharp"
|
11
|
+
s.authors = [ "Nicolas Szalay", "Jonathan Amiez" ]
|
12
|
+
s.email = [ "nico@rottenbytes.info", "jonathan.amiez@gmail.com" ]
|
13
|
+
s.license = "3-BSD"
|
14
|
+
|
15
|
+
s.files = %x(git ls-files).split("\n")
|
16
|
+
s.require_paths = [ "lib" ]
|
17
|
+
|
18
|
+
s.add_dependency "chef", ">= 11"
|
19
|
+
s.add_dependency "grit", "~> 2.5"
|
20
|
+
end
|
@@ -1,8 +1,10 @@
|
|
1
1
|
require 'chef/knife'
|
2
|
+
require 'knife-sharp/common'
|
2
3
|
require 'grit'
|
3
4
|
|
4
5
|
module KnifeSharp
|
5
6
|
class SharpAlign < Chef::Knife
|
7
|
+
include KnifeSharp::Common
|
6
8
|
|
7
9
|
banner "knife sharp align BRANCH ENVIRONMENT [OPTS]"
|
8
10
|
|
@@ -26,483 +28,42 @@ module KnifeSharp
|
|
26
28
|
:description => "dump items not present locally (roles/databags)",
|
27
29
|
:default => false
|
28
30
|
|
29
|
-
deps do
|
30
|
-
require 'chef/environment'
|
31
|
-
require 'chef/role'
|
32
|
-
require 'chef/cookbook/metadata'
|
33
|
-
require 'chef/cookbook_loader'
|
34
|
-
require 'chef/cookbook_uploader'
|
35
|
-
end
|
36
|
-
|
37
31
|
def run
|
38
|
-
setup()
|
39
|
-
ui.msg(ui.color("On server #{@chef_server}", :bold)) if @chef_server
|
40
|
-
check_cookbooks if @do_cookbooks
|
41
|
-
check_databags if @do_databags
|
42
|
-
check_roles if @do_roles
|
43
|
-
|
44
|
-
# All questions asked, can we proceed ?
|
45
|
-
if @cookbooks.empty? and @databags.empty? and @roles.empty?
|
46
|
-
ui.msg "Nothing else to do"
|
47
|
-
exit 0
|
48
|
-
end
|
49
|
-
|
50
|
-
ui.confirm(ui.color("> Proceed ", :bold))
|
51
|
-
bump_cookbooks if @do_cookbooks
|
52
|
-
update_databags if @do_databags
|
53
|
-
update_roles if @do_roles
|
54
|
-
end
|
55
|
-
|
56
|
-
def setup
|
57
32
|
# Checking args
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
33
|
+
ensure_branch_and_environment_provided!
|
34
|
+
|
35
|
+
# Checking repo branch
|
36
|
+
ensure_correct_branch_provided!
|
62
37
|
|
63
38
|
# check cli flags
|
64
39
|
if config[:cookbooks] or config[:databags] or config[:roles]
|
65
|
-
|
66
|
-
else
|
67
|
-
@do_cookbooks, @do_databags, @do_roles = true, true, true
|
68
|
-
end
|
69
|
-
|
70
|
-
# Sharp config
|
71
|
-
cfg_files = [ "/etc/sharp-config.yml", "~/.chef/sharp-config.yml" ]
|
72
|
-
loaded = false
|
73
|
-
cfg_files.each do |cfg_file|
|
74
|
-
begin
|
75
|
-
@cfg = YAML::load_file(File.expand_path(cfg_file))
|
76
|
-
loaded = true
|
77
|
-
rescue Exception => e
|
78
|
-
ui.error "Error on loading config : #{e.inspect}" if config[:verbosity] > 0
|
79
|
-
end
|
80
|
-
end
|
81
|
-
unless loaded == true
|
82
|
-
ui.error "config could not be loaded ! Tried the following files : #{cfg_files.join(", ")}"
|
83
|
-
exit 1
|
84
|
-
end
|
85
|
-
|
86
|
-
# Env setup
|
87
|
-
@branch, @environment = name_args
|
88
|
-
@chef_path = @cfg["global"]["git_cookbook_path"]
|
89
|
-
|
90
|
-
# Checking current branch
|
91
|
-
current_branch = Grit::Repo.new(@chef_path).head.name
|
92
|
-
if @branch != current_branch then
|
93
|
-
ui.error "Git repo is actually on branch #{current_branch} but you want to align using #{@branch}. Checkout to the desired one."
|
94
|
-
exit 1
|
95
|
-
end
|
96
|
-
|
97
|
-
# Knife config
|
98
|
-
if Chef::Knife.chef_config_dir && File.exists?(File.join(Chef::Knife.chef_config_dir, "knife.rb"))
|
99
|
-
Chef::Config.from_file(File.join(Chef::Knife.chef_config_dir, "knife.rb"))
|
100
|
-
else
|
101
|
-
ui.error "Cannot find knife.rb config file"
|
102
|
-
exit 1
|
103
|
-
end
|
104
|
-
|
105
|
-
# Logger
|
106
|
-
if @cfg["logging"]["enabled"]
|
107
|
-
begin
|
108
|
-
require "logger"
|
109
|
-
log_file = File.expand_path(@cfg["logging"]["destination"])
|
110
|
-
@log = Logger.new(log_file)
|
111
|
-
rescue Exception => e
|
112
|
-
ui.error "Unable to set up logger (#{e.inspect})."
|
113
|
-
exit 1
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
chefcfg = Chef::Config
|
118
|
-
@cb_path = chefcfg.cookbook_path.is_a?(Array) ? chefcfg.cookbook_path.first : chefcfg.cookbook_path
|
119
|
-
@db_path = chefcfg.data_bag_path.is_a?(Array) ? chefcfg.data_bag_path.first : chefcfg.data_bag_path
|
120
|
-
@role_path = chefcfg.role_path.is_a?(Array) ? chefcfg.role_path.first : chefcfg.role_path
|
121
|
-
|
122
|
-
@chef_server = SharpServer.new.current_server
|
123
|
-
@loader = Chef::CookbookLoader.new(@cb_path)
|
124
|
-
|
125
|
-
@cookbooks = Array.new
|
126
|
-
@databags = Hash.new
|
127
|
-
@roles = Hash.new
|
128
|
-
end
|
129
|
-
|
130
|
-
### Cookbook methods ###
|
131
|
-
|
132
|
-
def check_cookbooks
|
133
|
-
unless File.exists?(@cb_path)
|
134
|
-
ui.warn "Bad cookbook path, skipping cookbook sync."
|
135
|
-
return
|
136
|
-
end
|
137
|
-
|
138
|
-
ui.msg(ui.color("== Cookbooks ==", :bold))
|
139
|
-
|
140
|
-
updated_versions = Hash.new
|
141
|
-
local_versions = Hash[Dir.glob("#{@cb_path}/*").select {|cb| File.directory?(cb)}.map {|cb| [File.basename(cb), @loader[File.basename(cb)].version] }]
|
142
|
-
remote_versions = Chef::Environment.load(@environment).cookbook_versions.each_value {|v| v.gsub!("= ", "")}
|
143
|
-
|
144
|
-
if local_versions.empty?
|
145
|
-
ui.warn "No local cookbooks found, is the cookbook path correct ? (#{@cb_path})"
|
146
|
-
return
|
147
|
-
end
|
148
|
-
|
149
|
-
# get local-only cookbooks
|
150
|
-
(local_versions.keys - remote_versions.keys).each do |cb|
|
151
|
-
updated_versions[cb] = local_versions[cb]
|
152
|
-
ui.msg "* #{cb} is local only (version #{local_versions[cb]})"
|
153
|
-
end
|
154
|
-
|
155
|
-
# get cookbooks not up-to-date
|
156
|
-
(remote_versions.keys & local_versions.keys).each do |cb|
|
157
|
-
if Chef::VersionConstraint.new("> #{remote_versions[cb]}").include?(local_versions[cb])
|
158
|
-
updated_versions[cb] = local_versions[cb]
|
159
|
-
ui.msg "* #{cb} is not up-to-date (local: #{local_versions[cb]}/remote: #{remote_versions[cb]})"
|
160
|
-
elsif Chef::VersionConstraint.new("> #{local_versions[cb]}").include?(remote_versions[cb]) and config[:force_align]
|
161
|
-
updated_versions[cb] = local_versions[cb]
|
162
|
-
ui.msg "* #{cb} is to be downgraded (local: #{local_versions[cb]}/remote: #{remote_versions[cb]})"
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
if @cfg[@chef_server] and @cfg[@chef_server].has_key?("ignore_cookbooks")
|
167
|
-
(updated_versions.keys & @cfg[@chef_server]["ignore_cookbooks"]).each do |cb|
|
168
|
-
updated_versions.delete(cb)
|
169
|
-
ui.msg "* Skipping #{cb} cookbook (ignore list)"
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
if !updated_versions.empty?
|
174
|
-
all = false
|
175
|
-
updated_versions.each_pair do |cb,version|
|
176
|
-
answer = ui.ask_question("> Update #{cb} cookbook to #{version} on server ? Y/N/(A)ll/(Q)uit ", :default => "N").upcase unless all
|
177
|
-
|
178
|
-
if answer == "A"
|
179
|
-
all = true
|
180
|
-
elsif answer == "Q"
|
181
|
-
ui.msg "* Skipping next cookbooks alignment."
|
182
|
-
break
|
183
|
-
end
|
184
|
-
|
185
|
-
if all or answer == "Y"
|
186
|
-
@cookbooks << cb
|
187
|
-
else
|
188
|
-
ui.msg "* Skipping #{cb} cookbook"
|
189
|
-
end
|
190
|
-
end
|
191
|
-
else
|
192
|
-
ui.msg "* Environment #{@environment} is up-to-date."
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
|
197
|
-
def bump_cookbooks
|
198
|
-
unless @cookbooks.empty?
|
199
|
-
env = Chef::Environment.load(@environment)
|
200
|
-
cbs = Array.new
|
201
|
-
backup_data = Hash.new
|
202
|
-
backup_data["environment"] = @environment
|
203
|
-
backup_data["cookbook_versions"] = Hash.new
|
204
|
-
@cookbooks.each do |cb_name|
|
205
|
-
cb = @loader[cb_name]
|
206
|
-
if @cfg["rollback"] && @cfg["rollback"]["enabled"] == true
|
207
|
-
backup_data["cookbook_versions"][cb_name] = env.cookbook_versions[cb_name]
|
208
|
-
end
|
209
|
-
# Force "= a.b.c" in cookbook version, as chef11 will not accept "a.b.c"
|
210
|
-
env.cookbook_versions[cb_name] = "= #{cb.version}"
|
211
|
-
cbs << cb
|
212
|
-
end
|
213
|
-
|
214
|
-
ui.msg "* Uploading cookbook(s) #{@cookbooks.join(", ")}"
|
215
|
-
cookbook_uploader(cbs).upload_cookbooks
|
216
|
-
|
217
|
-
|
218
|
-
if env.save
|
219
|
-
cbs.each do |cb|
|
220
|
-
ui.msg "* Bumping #{cb.name} to #{cb.version} for environment #{@environment}"
|
221
|
-
log_action("bumping #{cb.name} to #{cb.version} for environment #{@environment}")
|
222
|
-
end
|
223
|
-
end
|
224
|
-
|
225
|
-
if @cfg["rollback"] && @cfg["rollback"]["enabled"] == true
|
226
|
-
identifier = Time.now.to_i
|
227
|
-
Dir.mkdir(@cfg["rollback"]["destination"]) unless File.exists?(@cfg["rollback"]["destination"])
|
228
|
-
fp = open(File.join(@cfg["rollback"]["destination"], "#{identifier}.json"), "w")
|
229
|
-
fp.write(JSON.pretty_generate(backup_data))
|
230
|
-
fp.close()
|
231
|
-
end
|
232
|
-
end
|
233
|
-
end
|
234
|
-
|
235
|
-
### Databag methods ###
|
236
|
-
|
237
|
-
def check_databags
|
238
|
-
unless File.exists?(@db_path)
|
239
|
-
ui.warn "Bad data bag path, skipping data bag sync."
|
240
|
-
return
|
241
|
-
end
|
242
|
-
|
243
|
-
ui.msg(ui.color("== Data bags ==", :bold))
|
244
|
-
|
245
|
-
updated_dbs = Hash.new
|
246
|
-
local_dbs = Dir.glob(File.join(@db_path, "**/*.json")).map {|f| [File.dirname(f).split("/").last, File.basename(f, ".json")]}
|
247
|
-
remote_dbs = Chef::DataBag.list.keys.map {|db| Chef::DataBag.load(db).keys.map{|dbi| [db, dbi]}}.flatten(1)
|
248
|
-
|
249
|
-
if local_dbs.empty?
|
250
|
-
ui.warn "No local data bags found, is the data bag path correct ? (#{@db_path})"
|
251
|
-
return
|
252
|
-
end
|
253
|
-
|
254
|
-
# Dump missing data bags locally
|
255
|
-
(remote_dbs - local_dbs).each do |db|
|
256
|
-
ui.msg "* #{db.join("/")} data bag item is remote only"
|
257
|
-
if config[:dump_remote_only]
|
258
|
-
ui.msg "* Dumping to #{File.join(@db_path, "#{db.join("/")}.json")}"
|
259
|
-
begin
|
260
|
-
remote_db = Chef::DataBagItem.load(db.first, db.last).raw_data
|
261
|
-
Dir.mkdir(File.join(@db_path, db.first)) unless File.exists?(File.join(@db_path, db.first))
|
262
|
-
File.open(File.join(@db_path, "#{db.join("/")}.json"), "w") do |file|
|
263
|
-
file.puts JSON.pretty_generate(remote_db)
|
264
|
-
end
|
265
|
-
rescue Exception => e
|
266
|
-
ui.error "Unable to dump #{db.join("/")} data bag item (#{e.message})"
|
267
|
-
end
|
268
|
-
end
|
269
|
-
end
|
270
|
-
|
271
|
-
# Create new data bags on server
|
272
|
-
(local_dbs - remote_dbs).each do |db|
|
273
|
-
begin
|
274
|
-
local_db = JSON::load(File.read(File.join(@db_path, "#{db.join("/")}.json")))
|
275
|
-
updated_dbs[db] = local_db
|
276
|
-
ui.msg "* #{db.join("/")} data bag item is local only"
|
277
|
-
rescue Exception => e
|
278
|
-
ui.error "Unable to load #{db.join("/")} data bag item (#{e.message})"
|
279
|
-
end
|
280
|
-
end
|
281
|
-
|
282
|
-
# Compare roles common to local and remote
|
283
|
-
(remote_dbs & local_dbs).each do |db|
|
284
|
-
begin
|
285
|
-
remote_db = Chef::DataBagItem.load(db.first, db.last).raw_data
|
286
|
-
local_db = JSON::load(File.read(File.join(@db_path, "#{db.join("/")}.json")))
|
287
|
-
if remote_db != local_db
|
288
|
-
updated_dbs[db] = local_db
|
289
|
-
ui.msg("* #{db.join("/")} data bag item is not up-to-date")
|
290
|
-
end
|
291
|
-
rescue Exception => e
|
292
|
-
ui.error "Unable to load #{db.join("/")} data bag item (#{e.message})"
|
293
|
-
end
|
294
|
-
end
|
295
|
-
|
296
|
-
if @cfg[@chef_server] and @cfg[@chef_server].has_key?("ignore_databags")
|
297
|
-
(updated_dbs.keys.map{|k| k.join("/")} & @cfg[@chef_server]["ignore_databags"]).each do |db|
|
298
|
-
updated_dbs.delete(db.split("/"))
|
299
|
-
ui.msg "* Skipping #{db} data bag (ignore list)"
|
300
|
-
end
|
301
|
-
end
|
302
|
-
|
303
|
-
if !updated_dbs.empty?
|
304
|
-
all = false
|
305
|
-
updated_dbs.each do |name, obj|
|
306
|
-
answer = nil
|
307
|
-
answer = ui.ask_question("> Update #{name.join("/")} data bag item on server ? Y/N/(A)ll/(Q)uit ", :default => "N").upcase unless all
|
308
|
-
|
309
|
-
if answer == "A"
|
310
|
-
all = true
|
311
|
-
elsif answer == "Q"
|
312
|
-
ui.msg "* Aborting data bag alignment."
|
313
|
-
break
|
314
|
-
end
|
315
|
-
|
316
|
-
if all or answer == "Y"
|
317
|
-
@databags[name] = obj
|
318
|
-
else
|
319
|
-
ui.msg "* Skipping #{name.join("/")} data bag item"
|
320
|
-
end
|
321
|
-
end
|
322
|
-
else
|
323
|
-
ui.msg "* Data bags are up-to-date."
|
324
|
-
end
|
325
|
-
end
|
326
|
-
|
327
|
-
def update_databags
|
328
|
-
parent_databags = Chef::DataBag.list.keys
|
329
|
-
unless @databags.empty?
|
330
|
-
@databags.each do |name, obj|
|
331
|
-
begin
|
332
|
-
# create the parent if needed
|
333
|
-
unless parent_databags.include?(name.first)
|
334
|
-
db = Chef::DataBag.new
|
335
|
-
db.name(name.first)
|
336
|
-
db.create
|
337
|
-
# add it to the list to avoid trying to recreate it
|
338
|
-
parent_databags.push(name.first)
|
339
|
-
ui.msg("* Creating data bag #{name.first}")
|
340
|
-
log_action("creating data bag #{name.first}")
|
341
|
-
end
|
342
|
-
db = Chef::DataBagItem.new
|
343
|
-
db.data_bag(name.first)
|
344
|
-
db.raw_data = obj
|
345
|
-
db.save
|
346
|
-
ui.msg "* Updating #{name.join("/")} data bag item"
|
347
|
-
log_action("updating #{name.join("/")} data bag item")
|
348
|
-
rescue Exception => e
|
349
|
-
ui.error "Unable to update #{name.join("/")} data bag item"
|
350
|
-
end
|
351
|
-
end
|
352
|
-
end
|
353
|
-
end
|
354
|
-
|
355
|
-
### Role methods ###
|
356
|
-
|
357
|
-
def check_roles
|
358
|
-
# role sections to compare (methods)
|
359
|
-
to_check = {
|
360
|
-
"env_run_lists" => "run list",
|
361
|
-
"default_attributes" => "default attributes",
|
362
|
-
"override_attributes" => "override attributes"
|
363
|
-
}
|
364
|
-
|
365
|
-
unless File.exists?(@role_path)
|
366
|
-
ui.warn "Bad role path, skipping role sync."
|
367
|
-
return
|
368
|
-
end
|
369
|
-
|
370
|
-
ui.msg(ui.color("== Roles ==", :bold))
|
371
|
-
|
372
|
-
updated_roles = Hash.new
|
373
|
-
local_roles = Dir.glob(File.join(@role_path, "*.json")).map {|file| File.basename(file, ".json")}
|
374
|
-
remote_roles = Chef::Role.list.keys
|
375
|
-
|
376
|
-
if local_roles.empty?
|
377
|
-
ui.warn "No local roles found, is the role path correct ? (#{@role_path})"
|
378
|
-
return
|
379
|
-
end
|
380
|
-
|
381
|
-
# Dump missing roles locally
|
382
|
-
(remote_roles - local_roles).each do |role|
|
383
|
-
ui.msg "* #{role} role is remote only"
|
384
|
-
if config[:dump_remote_only]
|
385
|
-
ui.msg "* Dumping to #{File.join(@role_path, "#{role}.json")}"
|
386
|
-
begin
|
387
|
-
remote_role = Chef::Role.load(role)
|
388
|
-
File.open(File.join(@role_path, "#{role}.json"), "w") do |file|
|
389
|
-
file.puts JSON.pretty_generate(remote_role)
|
390
|
-
end
|
391
|
-
rescue Exception => e
|
392
|
-
ui.error "Unable to dump #{role} role (#{e.message})"
|
393
|
-
end
|
394
|
-
end
|
395
|
-
end
|
396
|
-
|
397
|
-
# Create new roles on server
|
398
|
-
(local_roles - remote_roles).each do |role|
|
399
|
-
begin
|
400
|
-
local_role = Chef::Role.from_disk(role)
|
401
|
-
updated_roles[role] = local_role
|
402
|
-
ui.msg "* #{role} role is local only"
|
403
|
-
rescue Exception => e
|
404
|
-
ui.error "Unable to load #{role} role (#{e.message})"
|
405
|
-
end
|
406
|
-
end
|
407
|
-
|
408
|
-
# Compare roles common to local and remote
|
409
|
-
(remote_roles & local_roles).each do |role|
|
410
|
-
remote_role = Chef::Role.load(role)
|
411
|
-
local_role = Chef::Role.from_disk(role)
|
412
|
-
|
413
|
-
diffs = Array.new
|
414
|
-
to_check.each do |method, display|
|
415
|
-
if remote_role.send(method) != local_role.send(method)
|
416
|
-
updated_roles[role] = local_role
|
417
|
-
diffs << display
|
418
|
-
end
|
419
|
-
end
|
420
|
-
ui.msg("* #{role} role is not up-to-date (#{diffs.join(",")})") unless diffs.empty?
|
421
|
-
end
|
422
|
-
|
423
|
-
if @cfg[@chef_server] and @cfg[@chef_server].has_key?("ignore_roles")
|
424
|
-
(updated_roles.keys & @cfg[@chef_server]["ignore_roles"]).each do |r|
|
425
|
-
updated_roles.delete(r)
|
426
|
-
ui.msg "* Skipping #{r} role (ignore list)"
|
427
|
-
end
|
428
|
-
end
|
429
|
-
|
430
|
-
if !updated_roles.empty?
|
431
|
-
all = false
|
432
|
-
updated_roles.each do |name, obj|
|
433
|
-
answer = ui.ask_question("> Update #{name} role on server ? Y/N/(A)ll/(Q)uit ", :default => "N").upcase unless all
|
434
|
-
|
435
|
-
if answer == "A"
|
436
|
-
all = true
|
437
|
-
elsif answer == "Q"
|
438
|
-
ui.msg "* Aborting role alignment."
|
439
|
-
break
|
440
|
-
end
|
441
|
-
|
442
|
-
if all or answer == "Y"
|
443
|
-
@roles[name] = obj
|
444
|
-
else
|
445
|
-
ui.msg "* Skipping #{name} role"
|
446
|
-
end
|
447
|
-
end
|
40
|
+
do_cookbooks, do_databags, do_roles = config[:cookbooks], config[:databags], config[:roles]
|
448
41
|
else
|
449
|
-
|
42
|
+
do_cookbooks, do_databags, do_roles = true, true, true
|
450
43
|
end
|
451
|
-
end
|
452
44
|
|
453
|
-
|
454
|
-
unless @roles.empty?
|
455
|
-
@roles.each do |name, obj|
|
456
|
-
begin
|
457
|
-
obj.save
|
458
|
-
ui.msg "* Updating #{name} role"
|
459
|
-
log_action("updating #{name} role")
|
460
|
-
rescue Exception => e
|
461
|
-
ui.error "Unable to update #{name} role"
|
462
|
-
end
|
463
|
-
end
|
464
|
-
end
|
465
|
-
end
|
466
|
-
|
467
|
-
### Utility methods ###
|
468
|
-
|
469
|
-
def log_action(message)
|
470
|
-
# log file if enabled
|
471
|
-
log_message = message
|
472
|
-
log_message += " on server #{@chef_server}" if @chef_server
|
473
|
-
@log.info(log_message) if @cfg["logging"]["enabled"]
|
45
|
+
ui.msg(ui.color("On server #{chef_server}", :bold)) if chef_server
|
474
46
|
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
end
|
485
|
-
end
|
486
|
-
end
|
47
|
+
SharpCookbookAlign.load_deps
|
48
|
+
sca = SharpCookbookAlign.new
|
49
|
+
cookbooks_to_update = do_cookbooks ? sca.check_cookbooks(environment) : []
|
50
|
+
SharpDataBagAlign.load_deps
|
51
|
+
sda = SharpDataBagAlign.new
|
52
|
+
databags_to_update = do_databags ? sda.check_databags : {}
|
53
|
+
SharpRoleAlign.load_deps
|
54
|
+
sra = SharpRoleAlign.new
|
55
|
+
roles_to_update = do_roles ? sra.check_roles : {}
|
487
56
|
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
uri = URI.parse("#{config["url"]}/#{config["channel"]}")
|
493
|
-
notif = "chef: #{message} by #{config["username"]}"
|
494
|
-
Net::HTTP.post_form(uri, { "message" => notif })
|
495
|
-
rescue
|
496
|
-
ui.error "Unable to notify via hubot."
|
57
|
+
# All questions asked, can we proceed ?
|
58
|
+
if cookbooks_to_update.empty? and databags_to_update.empty? and roles_to_update.empty?
|
59
|
+
ui.msg "Nothing else to do"
|
60
|
+
exit 0
|
497
61
|
end
|
498
|
-
end
|
499
62
|
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
uploader = Chef::CookbookUploader.new(cookbooks, Chef::Config.cookbook_path)
|
505
|
-
end
|
63
|
+
ui.confirm(ui.color("> Proceed ", :bold))
|
64
|
+
sca.bump_cookbooks(environment, cookbooks_to_update) if do_cookbooks
|
65
|
+
sda.update_databags(databags_to_update) if do_databags
|
66
|
+
sra.update_roles(roles_to_update) if do_roles
|
506
67
|
end
|
507
68
|
end
|
508
69
|
end
|