knife-sharp 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,143 @@
1
+ # Knife sharp plugin
2
+
3
+ knife-sharp adds several handy features to knife, adapted to our workflow @ Fotolia.
4
+ Features:
5
+ * align : sync data bags, roles and cookbook versions between a local git branches and a chef server
6
+ * backup : dump environments, roles and data bags to local json files
7
+ * server : switch between chef servers using multiple knife.rb config files
8
+
9
+ # Tell me more
10
+
11
+ When you want an environment to reflect a given branch you have to check by hand (or using our consistency plugin), and some mistakes can be made. This plugin aims to help to push the right version into an environment.
12
+
13
+ It also allows to adopt a review workflow for main chef components :
14
+ * Track data bags, roles (as JSON files) and cookbooks in your Chef git repository
15
+ * Push each modification of any to peer-review
16
+ * Once merged, upload every change with knife sharp align
17
+
18
+ # Show me !
19
+
20
+ ## Align
21
+
22
+ <pre>
23
+ $ git branch
24
+ ...
25
+ master
26
+ * syslog_double
27
+ ...
28
+ $ knife environment show sandboxnico
29
+ chef_type: environment
30
+ cookbook_versions:
31
+ ...
32
+ syslog: 0.0.16
33
+ ...
34
+ $ knife sharp align syslog_double sandboxnico
35
+
36
+ Will change in environment sandboxnico :
37
+ * syslog gets version 0.0.17
38
+ Upload and set version into environment sandboxnico ? Y/N
39
+ Y
40
+ Successfull upload for syslog
41
+ Aligning 1 cookbooks
42
+ Aligning data bags
43
+ * infrastructure/mail data bag item is not up-to-date
44
+ Update infrastructure/mail data bag item on server ? Y/N/(A)ll/(Q)uit [N] n
45
+ * Skipping infrastructure/mail data bag item
46
+ Aligning roles
47
+ * Dev_Server role is not up-to-date (run list)
48
+ Update Dev_Server role on server ? Y/N/(A)ll/(Q)uit [N] n
49
+ * Skipping Dev_Server role
50
+ </pre>
51
+
52
+ Then we can check environment :
53
+
54
+ <pre>
55
+ $ knife environment show sandboxnico
56
+ chef_type: environment
57
+ cookbook_versions:
58
+ ...
59
+ syslog: 0.0.17
60
+ ...
61
+ $ knife sharp align syslog_double sandboxnico
62
+ Nothing to do : sandboxnico has same versions as syslog_double
63
+ </pre>
64
+
65
+ It will upload the cookbooks (to ensure they meet the one on the branch you're working on) and will set the version to the required number.
66
+
67
+ ## Backup
68
+
69
+ Making a backup before a large change can be a lifesaver. Knife sharp can do it for you, easily
70
+ <pre>
71
+ $ knife sharp backup
72
+ Backing up roles
73
+ Backing up environments
74
+ Backing up databags
75
+ $
76
+ </pre>
77
+
78
+ All these items get stored in the place defined in your config file.
79
+
80
+ ## Server
81
+
82
+ <pre>
83
+ $ knife sharp server
84
+ Available servers:
85
+ prod (/home/jamiez/.chef/knife-prod.rb)
86
+ >> dev (/home/jamiez/.chef/knife-dev.rb)
87
+
88
+ $ knife sharp server prod
89
+ The knife configuration has been updated to use prod.
90
+
91
+ $ knife sharp server
92
+ Available servers:
93
+ >> prod (/home/jamiez/.chef/knife-prod.rb)
94
+ dev (/home/jamiez/.chef/knife-dev.rb)
95
+ </pre>
96
+
97
+ # Configuration
98
+
99
+ Dependencies :
100
+ * grit
101
+
102
+ The plugin will search in 2 places for its config file :
103
+ * "/etc/sharp-config.yml"
104
+ * "~/.chef/sharp-config.yml"
105
+
106
+ An example config file is provided in ext/.
107
+
108
+ A working knife setup is also required (cookbook/role/data bag paths depending on the desired features)
109
+
110
+ ## Cookbooks path & git
111
+ If your cookbook_path is not the root of your git directory then the grit gem will produce an error. This can be circumvented by adding the following directive in your config file :
112
+
113
+ <pre>
114
+ global:
115
+ git_cookbook_path: "/home/nico/sysadmin/chef/"
116
+ </pre>
117
+
118
+ As we version more than the cookbooks in the repo.
119
+
120
+ ## Logging
121
+ It's good to have things logged. The plugin can do it for you. Add this to your config file
122
+ <pre>
123
+ logging:
124
+ enabled: true
125
+ destination: "~/.chef/sharp.log"
126
+ </pre>
127
+
128
+ It will log uploads, bumps and databags to the standard logger format.
129
+
130
+ # Credits
131
+
132
+ The damn good knife spork plugin from the etsy folks : https://github.com/jonlives/knife-spork
133
+
134
+ Idea for knife sharp server comes from https://github.com/greenandsecure/knife-block
135
+
136
+ License
137
+ =======
138
+ 3 clauses BSD
139
+
140
+ Authors
141
+ ======
142
+ Nicolas Szalay | https://github.com/rottenbytes
143
+ Jonathan Amiez | https://github.com/josqu4red
@@ -0,0 +1,12 @@
1
+ logging:
2
+ enabled: true
3
+ destination: "~/.chef/sharp.log"
4
+ global:
5
+ git_cookbook_path: "/home/nico/sysadmin/chef/"
6
+ backupdir: "/home/nico/.chef/sharp/backups/"
7
+ notification:
8
+ hubot:
9
+ enabled: true
10
+ url: "http://your.hubot.lan:18080/hubot/chef/bump/"
11
+ channel: "foo"
12
+ username: "Nicolas"
@@ -0,0 +1,359 @@
1
+ require 'chef/knife'
2
+ require 'grit'
3
+
4
+ module KnifeSharp
5
+ class SharpAlign < Chef::Knife
6
+
7
+ banner "knife sharp align BRANCH ENVIRONMENT [--debug] [--quiet]"
8
+
9
+ option :debug,
10
+ :short => '-d',
11
+ :long => '--debug',
12
+ :description => "turn debug on",
13
+ :default => false
14
+
15
+ option :quiet,
16
+ :short => '-q',
17
+ :long => '--quiet',
18
+ :description => 'does not notifies',
19
+ :default => false
20
+
21
+ deps do
22
+ require 'chef/cookbook/metadata'
23
+ require 'chef/cookbook_loader'
24
+ require 'chef/cookbook_uploader'
25
+ end
26
+
27
+ def run
28
+ setup()
29
+ ui.msg "Aligning cookbooks"
30
+ align_cookbooks()
31
+ ui.msg "Aligning data bags"
32
+ align_databags()
33
+ ui.msg "Aligning roles"
34
+ align_roles()
35
+ end
36
+
37
+ def setup
38
+ # Checking args
39
+ if name_args.count != 2
40
+ ui.error "Usage : knife align BRANCH ENVIRONMENT [--debug]"
41
+ exit 1
42
+ end
43
+
44
+ # Sharp config
45
+ cfg_files = [ "/etc/sharp-config.yml", "~/.chef/sharp-config.yml" ]
46
+ loaded = false
47
+ cfg_files.each do |cfg_file|
48
+ begin
49
+ @cfg = YAML::load_file(File.expand_path(cfg_file))
50
+ loaded = true
51
+ rescue Exception => e
52
+ ui.error "Error on loading config : #{e.inspect}" if config[:debug]
53
+ end
54
+ end
55
+ unless loaded == true
56
+ ui.error "config could not be loaded ! Tried the following files : #{cfg_files.join(", ")}"
57
+ exit 1
58
+ end
59
+
60
+ # Knife config
61
+ if Chef::Knife.chef_config_dir && File.exists?(File.join(Chef::Knife.chef_config_dir, "knife.rb"))
62
+ Chef::Config.from_file(File.join(Chef::Knife.chef_config_dir, "knife.rb"))
63
+ else
64
+ ui.error "Cannot find knife.rb config file"
65
+ exit 1
66
+ end
67
+
68
+ # Env setup
69
+ @branch, @environment = name_args
70
+ @chef_path = @cfg["global"]["git_cookbook_path"]
71
+
72
+ chefcfg = Chef::Config
73
+ @cb_path = chefcfg.cookbook_path.is_a?(Array) ? chefcfg.cookbook_path.first : chefcfg.cookbook_path
74
+ @db_path = chefcfg.data_bag_path.is_a?(Array) ? chefcfg.data_bag_path.first : chefcfg.data_bag_path
75
+ @role_path = chefcfg.role_path.is_a?(Array) ? chefcfg.role_path.first : chefcfg.role_path
76
+
77
+ # Checking current branch
78
+ current_branch = Grit::Repo.new(@chef_path).head.name
79
+ if @branch != current_branch then
80
+ ui.error "Git repo is actually on branch #{current_branch} but you want to align using #{@branch}. Checkout to the desired one."
81
+ exit 1
82
+ end
83
+ end
84
+
85
+ ### Cookbook methods ###
86
+
87
+ def align_cookbooks
88
+ updated_versions = Hash.new()
89
+ local_versions = get_cookbook_local_versions()
90
+ remote_versions = get_cookbook_versions_from_env(@environment)
91
+
92
+ (local_versions.keys - remote_versions.keys).each do |cb|
93
+ updated_versions[cb] = local_versions[cb]
94
+ ui.msg "* #{cb} is local only (version #{local_versions[cb]})"
95
+ end
96
+
97
+ (remote_versions.keys & local_versions.keys).each do |cb|
98
+ if remote_versions[cb] != local_versions[cb]
99
+ updated_versions[cb] = local_versions[cb]
100
+ ui.msg "* #{cb} is not up-to-date (local: #{local_versions[cb]}/remote: #{remote_versions[cb]})"
101
+ end
102
+ end
103
+
104
+ bumped = Hash.new
105
+ if !updated_versions.empty?
106
+ all = false
107
+ env = Chef::Environment.load(@environment)
108
+ loader = Chef::CookbookLoader.new(@cb_path)
109
+ updated_versions.each_pair do |cb,version|
110
+ answer = ui.ask_question("Update #{cb} cookbook item on server ? Y/N/(A)ll/(Q)uit ", :default => "N").upcase unless all
111
+
112
+ if answer == "A"
113
+ all = true
114
+ elsif answer == "Q"
115
+ ui.msg "> Aborting cookbook alignment."
116
+ break
117
+ end
118
+
119
+ if all or answer == "Y"
120
+ log_action("* Uploading cookbook #{cb} version #{version}")
121
+ cb_obj = loader[cb]
122
+ uploader = Chef::CookbookUploader.new(cb_obj, @cb_path)
123
+ uploader.upload_cookbooks
124
+ env.cookbook_versions[cb] = version
125
+ bumped[cb] = version
126
+ else
127
+ ui.msg "* Skipping #{cb} cookbook"
128
+ end
129
+ end
130
+ if env.save
131
+ bumped.each do |cb, version|
132
+ log_action("* Bumping cookbook #{cb} to #{version} for environment #{@environment}")
133
+ hubot_notify(cb, version, @environment)
134
+ end
135
+ end
136
+ else
137
+ ui.msg "> Environment #{@environment} is up-to-date."
138
+ end
139
+ end
140
+
141
+ # get cookbook for a known environment
142
+ def get_cookbook_versions_from_env(env_name)
143
+ Chef::Environment.load(env_name).cookbook_versions.each_value {|v| v.gsub!("= ", "")}
144
+ end
145
+
146
+ # in your local dealer !
147
+ def get_cookbook_local_versions
148
+ cbs = Hash.new
149
+ Dir.glob("#{@cb_path}/*").each do |cookbook|
150
+ md = Chef::Cookbook::Metadata.new
151
+ md.from_file("#{cookbook}/metadata.rb")
152
+ cbs[File.basename(cookbook)] = md.version
153
+ end
154
+ return cbs
155
+ end
156
+
157
+ ### Databag methods ###
158
+
159
+ def align_databags
160
+ unless File.exists?(@db_path)
161
+ ui.warn "Bad data bag path, skipping data bag sync."
162
+ return
163
+ end
164
+
165
+ updated_dbs = Hash.new
166
+ local_dbs = Dir.glob(File.join(@db_path, "**/*.json")).map {|f| [File.dirname(f).split("/").last, File.basename(f, ".json")]}
167
+ remote_dbs = Chef::DataBag.list.keys.map {|db| Chef::DataBag.load(db).keys.map{|dbi| [db, dbi]}}.flatten(1)
168
+
169
+ ui.warn "No local data bags found, is the role path correct ? (#{@role_path})" if local_dbs.empty?
170
+
171
+ # Create new data bags on server
172
+ (local_dbs - remote_dbs).each do |db|
173
+ ui.msg "+ #{db.join("/")} data bag item is local only. Creating"
174
+ begin
175
+ local_db = Chef::DataBagItem.new
176
+ local_db.data_bag(db.first)
177
+ local_db.raw_data = JSON::load(File.read(File.join(@db_path, "#{db.join("/")}.json")))
178
+ local_db.save
179
+ rescue Exception => e
180
+ ui.error "Unable to create #{db.join("/")} data bag item (#{e.message})"
181
+ end
182
+ end
183
+
184
+ # Dump missing data bags locally
185
+ (remote_dbs - local_dbs).each do |db|
186
+ ui.msg "- #{db.join("/")} data bag item is remote only. Dumping to #{File.join(@db_path, "#{db.join("/")}.json")}"
187
+ begin
188
+ remote_db = Chef::DataBagItem.load(db.first, db.last).raw_data
189
+ Dir.mkdir(File.join(@db_path, db.first)) unless Dir.exists?(File.join(@db_path, db.first))
190
+ File.open(File.join(@db_path, "#{db.join("/")}.json"), "w") do |file|
191
+ file.puts JSON.pretty_generate(remote_db)
192
+ end
193
+ rescue Exception => e
194
+ ui.error "Unable to dump #{db.join("/")} data bag item (#{e.message})"
195
+ end
196
+ end
197
+
198
+ # Compare roles common to local and remote
199
+ (remote_dbs & local_dbs).each do |db|
200
+ remote_db = Chef::DataBagItem.load(db.first, db.last).raw_data
201
+ local_db = JSON::load(File.read(File.join(@db_path, "#{db.join("/")}.json")))
202
+
203
+ if remote_db != local_db
204
+ updated_dbs[db] = local_db
205
+ ui.msg("* #{db.join("/")} data bag item is not up-to-date")
206
+ end
207
+ end
208
+
209
+ if !updated_dbs.empty?
210
+ all = false
211
+ updated_dbs.each do |name, obj|
212
+ answer = ui.ask_question("Update #{name.join("/")} data bag item on server ? Y/N/(A)ll/(Q)uit ", :default => "N").upcase unless all
213
+
214
+ if answer == "A"
215
+ all = true
216
+ elsif answer == "Q"
217
+ ui.msg "> Aborting data bag alignment."
218
+ break
219
+ end
220
+
221
+ if all or answer == "Y"
222
+ ui.msg "* Updating #{name.join("/")} data bag item"
223
+ db = Chef::DataBagItem.new
224
+ db.data_bag(name.first)
225
+ db.raw_data = obj
226
+ db.save
227
+ else
228
+ ui.msg "* Skipping #{name.join("/")} data bag item"
229
+ end
230
+ end
231
+ else
232
+ ui.msg "> Data bags are up-to-date."
233
+ end
234
+ end
235
+
236
+ ### Role methods ###
237
+
238
+ def align_roles
239
+ # role sections to compare (methods)
240
+ to_check = {
241
+ "env_run_lists" => "run list",
242
+ "default_attributes" => "default attributes",
243
+ "override_attributes" => "override attributes"
244
+ }
245
+
246
+ unless File.exists?(@role_path)
247
+ ui.warn "Bad role path, skipping role sync."
248
+ return
249
+ end
250
+
251
+ updated_roles = Hash.new
252
+ local_roles = Dir.glob(File.join(@role_path, "*.json")).map {|file| File.basename(file, ".json")}
253
+ remote_roles = Chef::Role.list.keys
254
+
255
+ ui.warn "No local roles found, is the role path correct ? (#{@role_path})" if local_roles.empty?
256
+
257
+ # Create new roles on server
258
+ (local_roles - remote_roles).each do |role|
259
+ ui.msg "+ #{role} role is local only. Creating"
260
+ begin
261
+ local_role = Chef::Role.from_disk(role)
262
+ local_role.save
263
+ rescue Exception => e
264
+ ui.error "Unable to create #{role} role (#{e.message})"
265
+ end
266
+ end
267
+
268
+ # Dump missing roles locally
269
+ (remote_roles - local_roles).each do |role|
270
+ ui.msg "- #{role} role is remote only. Dumping to #{File.join(@role_path, "#{role}.json")}"
271
+ begin
272
+ remote_role = Chef::Role.load(role)
273
+ File.open(File.join(@role_path, "#{role}.json"), "w") do |file|
274
+ file.puts JSON.pretty_generate(remote_role)
275
+ end
276
+ rescue Exception => e
277
+ ui.error "Unable to dump #{role} role (#{e.message})"
278
+ end
279
+ end
280
+
281
+ # Compare roles common to local and remote
282
+ (remote_roles & local_roles).each do |role|
283
+ remote_role = Chef::Role.load(role)
284
+ local_role = Chef::Role.from_disk(role)
285
+
286
+ diffs = Array.new
287
+ to_check.each do |method, display|
288
+ if remote_role.send(method) != local_role.send(method)
289
+ updated_roles[role] = local_role
290
+ diffs << display
291
+ end
292
+ end
293
+ ui.msg("* #{role} role is not up-to-date (#{diffs.join(",")})") unless diffs.empty?
294
+ end
295
+
296
+ if !updated_roles.empty?
297
+ all = false
298
+ updated_roles.each do |name, obj|
299
+ answer = ui.ask_question("Update #{name} role on server ? Y/N/(A)ll/(Q)uit ", :default => "N").upcase unless all
300
+
301
+ if answer == "A"
302
+ all = true
303
+ elsif answer == "Q"
304
+ ui.msg "> Aborting role alignment."
305
+ break
306
+ end
307
+
308
+ if all or answer == "Y"
309
+ ui.msg "* Updating #{name} role"
310
+ obj.save
311
+ else
312
+ ui.msg "* Skipping #{name} role"
313
+ end
314
+ end
315
+ else
316
+ ui.msg "> Roles are up-to-date."
317
+ end
318
+ end
319
+
320
+ ### Utility methods ###
321
+
322
+ def log_action(message)
323
+ ui.msg(message)
324
+
325
+ if @cfg["logging"]["enabled"]
326
+ begin
327
+ require "logger"
328
+ log_file = File.expand_path(@cfg["logging"]["destination"])
329
+ log = Logger.new(log_file)
330
+ log.info(message)
331
+ log.close
332
+ rescue Exception => e
333
+ ui.error "Oops ! #{e.inspect} ! message to log was #{message}"
334
+ end
335
+ end
336
+ end
337
+
338
+ def hubot_notify(cookbook, to_version, environment)
339
+ unless @cfg["notification"]["hubot"]["enabled"] == true and config[:quiet] == false
340
+ ui.msg "Aborting due to quiet or config disabled" if config[:debug]
341
+ return
342
+ end
343
+
344
+ begin
345
+ require "net/http"
346
+ require "uri"
347
+ uri = URI.parse(@cfg["notification"]["hubot"]["url"] + @cfg["notification"]["hubot"]["channel"])
348
+ user = @cfg["notification"]["hubot"]["username"]
349
+
350
+ Net::HTTP.post_form(uri, { "cookbook" => cookbook,
351
+ "user" => user,
352
+ "to_version" => to_version,
353
+ "environment" => environment })
354
+ rescue
355
+ ui.error "Oops ! could not notify hubot !"
356
+ end
357
+ end
358
+ end
359
+ end
@@ -0,0 +1,106 @@
1
+ require 'chef/knife'
2
+
3
+ module KnifeSharp
4
+ class SharpBackup < Chef::Knife
5
+
6
+ banner "knife sharp backup"
7
+
8
+ deps do
9
+ require 'chef/cookbook/metadata'
10
+ require 'chef/cookbook_loader'
11
+ require 'chef/cookbook_uploader'
12
+ end
13
+
14
+ def run
15
+ setup()
16
+ backup()
17
+ end
18
+
19
+ def setup
20
+ # Sharp config
21
+ cfg_files = [ "/etc/sharp-config.yml", "~/.chef/sharp-config.yml" ]
22
+ loaded = false
23
+ cfg_files.each do |cfg_file|
24
+ begin
25
+ @cfg = YAML::load_file(File.expand_path(cfg_file))
26
+ loaded = true
27
+ rescue Exception => e
28
+ ui.error "Error on loading config : #{e.inspect}" if config[:debug]
29
+ end
30
+ end
31
+ unless loaded == true
32
+ ui.error "config could not be loaded ! Tried the following files : #{cfg_files.join(", ")}"
33
+ exit 1
34
+ end
35
+
36
+ # Knife config
37
+ if (ENV['HOME'] && File.exist?(File.join(ENV['HOME'], '.chef', 'knife.rb')))
38
+ Chef::Config.from_file(File.join(ENV['HOME'], '.chef', 'knife.rb'))
39
+ else
40
+ ui.error "Cannot find knife.rb config file"
41
+ exit 1
42
+ end
43
+
44
+ end
45
+
46
+ def backup
47
+ timestamp = Time.now.to_i
48
+
49
+ unless @cfg["global"]["backupdir"]
50
+ ui.error "You need to add global/backupdir to your config file"
51
+ exit 1
52
+ end
53
+
54
+ backup_path = @cfg["global"]["backupdir"] + "/backup-#{timestamp}"
55
+
56
+ if File.exist?(backup_path)
57
+ ui.error "Backup path (#{backup_path}) exists. Will not overwrite."
58
+ exit 1
59
+ end
60
+
61
+ ui.msg("Backing up roles")
62
+ FileUtils.mkdir_p(backup_path + "/roles")
63
+ Chef::Role.list.keys.each do |role|
64
+ begin
65
+ remote_role = Chef::Role.load(role)
66
+ File.open(backup_path + "/roles/#{role}.json", "w") do |file|
67
+ file.puts JSON.pretty_generate(remote_role)
68
+ end
69
+ rescue Exception => e
70
+ ui.error "Unable to dump #{role} role (#{e.message})"
71
+ end
72
+ end
73
+
74
+ ui.msg("Backing up environments")
75
+ FileUtils.mkdir_p(backup_path + "/environments")
76
+ Chef::Environment.list.keys.each do |environment|
77
+ begin
78
+ env = Chef::Environment.load(environment)
79
+ File.open(backup_path + "/environments/#{environment}.json", "w") do |file|
80
+ file.puts JSON.pretty_generate(env)
81
+ end
82
+ rescue Exception => e
83
+ ui.error "Unable to dump #{environment} environment (#{e.message})"
84
+ end
85
+ end
86
+
87
+ ui.msg("Backing up databags")
88
+ FileUtils.mkdir_p(backup_path + "/databags")
89
+ Chef::DataBag.list.keys.each do |bag|
90
+ Dir.mkdir(backup_path + "/databags/" + bag)
91
+ Chef::DataBag.load(bag).keys.each do |item|
92
+ begin
93
+ data = Chef::DataBagItem.load(bag,item)
94
+ File.open(backup_path + "/databags/#{bag}/#{item}.json", "w") do |file|
95
+ file.puts JSON.pretty_generate(data)
96
+ end
97
+ rescue Exception => e
98
+ ui.error "Unable to dump item #{item} from databag #{bag} (#{e.message})"
99
+ end
100
+ end
101
+ end
102
+
103
+ end
104
+
105
+ end
106
+ end
@@ -0,0 +1,57 @@
1
+ module KnifeSharp
2
+ class SharpHistory < Chef::Knife
3
+ banner "knife sharp history"
4
+
5
+ option :debug,
6
+ :long => '--debug',
7
+ :description => "turn debug on",
8
+ :default => false
9
+
10
+ deps do
11
+ require 'chef/data_bag'
12
+ require 'chef/data_bag_item'
13
+ require 'chef/json_compat'
14
+ require 'chef/knife/search'
15
+ require 'chef/environment'
16
+ require 'chef/cookbook/metadata'
17
+ require 'chef/knife/core/object_loader'
18
+ end
19
+
20
+ @@cfg_files = [ "/etc/sharp-config.yml", "~/.chef/sharp-config.yml" ]
21
+
22
+ def load_config
23
+ loaded = false
24
+ @@cfg_files.each do |cfg_file|
25
+ begin
26
+ @@cfg=YAML::load_file(File.expand_path(cfg_file))
27
+ loaded = true
28
+ rescue Exception => e
29
+ puts "Error on loading config : #{e.inspect}" if config[:debug]
30
+ end
31
+ end
32
+ unless loaded == true
33
+ ui.error "config could not be loaded ! Tried the following files : #{@@cfg_files.join(", ")}"
34
+ exit 1
35
+ end
36
+ puts @@cfg.inspect if config[:debug]
37
+ end
38
+
39
+ def run
40
+ load_config()
41
+ show_logs()
42
+ end
43
+
44
+ def show_logs()
45
+ begin
46
+ fp = File.open(File.expand_path(@@cfg["logging"]["destination"]), "r")
47
+ fp.readlines.each do |line|
48
+ puts line
49
+ end
50
+ rescue Exception => e
51
+ ui.error "oops ! #{e.inspect}"
52
+ end
53
+ end
54
+
55
+
56
+ end
57
+ end
@@ -0,0 +1,54 @@
1
+ module KnifeSharp
2
+ class SharpServer < Chef::Knife
3
+
4
+ banner "knife sharp server [SERVERNAME]"
5
+
6
+ def run
7
+ if File.exists?(knife_conf)
8
+ if !File.symlink?(knife_conf)
9
+ ui.error "#{knife_conf} is not a symlink."
10
+ ui.msg "Copy it to #{knife_conf("<server name>")} and try again."
11
+ exit 1
12
+ end
13
+ end
14
+ unless name_args.size == 1
15
+ list_configs
16
+ exit 0
17
+ end
18
+
19
+ update_config(name_args.first)
20
+ end
21
+
22
+ def update_config(new_server)
23
+ if File.exists?(knife_conf(new_server))
24
+ File.unlink(knife_conf) if File.exists?(knife_conf)
25
+ File.symlink(knife_conf(new_server), knife_conf)
26
+ ui.msg "The knife configuration has been updated to use #{new_server}."
27
+ else
28
+ ui.error "Knife configuration for #{new_server} not found."
29
+ list_configs
30
+ exit 1
31
+ end
32
+ end
33
+
34
+ def list_configs
35
+ current_conf = File.readlink(knife_conf) if File.exists?(knife_conf)
36
+ avail_confs = Dir.glob(File.join(Chef::Knife::chef_config_dir, "knife-*.rb"))
37
+ if !avail_confs.empty?
38
+ ui.msg "Available servers:"
39
+ avail_confs.each do |file|
40
+ name = file.match(/#{Chef::Knife::chef_config_dir}\/knife-(?<srv>.*)\.rb/)
41
+ prefix = (file == current_conf) ? ">> " : " "
42
+ ui.msg "#{prefix}#{name[:srv]} (#{file})"
43
+ end
44
+ else
45
+ ui.msg "No knife server configuration file found."
46
+ end
47
+ end
48
+
49
+ def knife_conf(server = nil)
50
+ srv = server ? "-#{server}" : ""
51
+ File.join(Chef::Knife::chef_config_dir, "knife#{srv}.rb")
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,3 @@
1
+ module KnifeSharp
2
+ VERSION = "0.1.3"
3
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: knife-sharp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.3
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Nicolas Szalay
9
+ - Jonathan Amiez
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-10-12 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: chef
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ! '>='
29
+ - !ruby/object:Gem::Version
30
+ version: '0'
31
+ - !ruby/object:Gem::Dependency
32
+ name: grit
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ~>
37
+ - !ruby/object:Gem::Version
38
+ version: 2.5.0
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ version: 2.5.0
47
+ description: Sharpen your knife
48
+ email: nico@rottenbytes.info
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - README.md
54
+ - ext/sharp-config.yml
55
+ - lib/knife-sharp.rb
56
+ - lib/chef/knife/sharp-align.rb
57
+ - lib/chef/knife/sharp-backup.rb
58
+ - lib/chef/knife/sharp-history.rb
59
+ - lib/chef/knife/sharp-server.rb
60
+ homepage: http://www.rottenbytes.info
61
+ licenses: []
62
+ post_install_message:
63
+ rdoc_options: []
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ! '>='
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubyforge_project:
80
+ rubygems_version: 1.8.24
81
+ signing_key:
82
+ specification_version: 3
83
+ summary: Knife sharp plugin
84
+ test_files: []