knife-topo 1.1.0 → 1.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0be8df712b7387e67c6aba9af7e0433db88a6e56
4
- data.tar.gz: 4cbd85eb38a152561f1db568868c955010babced
3
+ metadata.gz: a5982dbdd98f486734b60e5f8f7d6db46c18daf2
4
+ data.tar.gz: 8ba53f9163e5c9883f7098319c9a3955d9c24d14
5
5
  SHA512:
6
- metadata.gz: 0cff13f858765854b3c791dcf15ce8fafcf054eeb99aab2eb04d1ecedb11a5343962c4c80631b2b2f9ecea8a29b384cde87200e65c2c58b17b12072754fa971d
7
- data.tar.gz: 1f5316d287e3e018a03c6cb1f210bd771dc660a02eb5477a2f65396b80160daf37ff2c34445a835c5a580426ac4c5c20e6600e8e7da322c2e29fbc89edf82fce
6
+ metadata.gz: 1a273459319db01f84a3bdfd1507ef5a1c9f201829087d439473fc38aeb2c68c1eec8c4699971b551081bb4f1ce2e6d85d1e7630eae07ba945326d073d833613
7
+ data.tar.gz: 644dd730a1e628f4adc3cf2d6f7b3e1f6dcacbb1f1e5e762df157aed9484a14aa0f998d583e760aa9337324ddca0ddc07d18bead4173488d59ed97a73cda9abb
data/README.md CHANGED
@@ -338,7 +338,7 @@ The following will create the 'test1' topology, and bootstrap it.
338
338
  The following will create the 'test1' topology but will not bootstrap it
339
339
  or upload topology cookbooks.
340
340
 
341
- $ knife topo create test1 --disable-upload
341
+ $ knife topo create test1 --disable-upload
342
342
 
343
343
  ## knife topo delete <a name="delete"></a>
344
344
 
@@ -378,17 +378,17 @@ Option | Description
378
378
  The following will export the data for nodes n1 and n2 as part of a
379
379
  topology called 'my_topo':
380
380
 
381
- $ knife topo export n1 n2 --topo=my_topo > new_topo.json
381
+ $ knife topo export n1 n2 --topo my_topo > new_topo.json
382
382
 
383
383
 
384
- The following will export all topologies to a file called 'all_topos.json'.
384
+ The following will export all existing topologies to a file called 'all_topos.json'.
385
385
 
386
386
  $ knife topo export --all > all_topos.json
387
387
 
388
388
  The following will create an outline for a new topology called
389
389
  'christine_test', or export the current details if it already exists:
390
390
 
391
- $ knife topo export --topo=christine_test > christine_test.json
391
+ $ knife topo export --topo christine_test > christine_test.json
392
392
 
393
393
 
394
394
  ## knife topo import <a name="import"></a>
@@ -1,5 +1,5 @@
1
1
  module Knife
2
2
  module Topo
3
- VERSION = "1.1.0"
3
+ VERSION = "1.1.2"
4
4
  end
5
5
  end
@@ -34,6 +34,11 @@ class Chef
34
34
  :short => '-D DATA_BAG',
35
35
  :long => "--data-bag DATA_BAG",
36
36
  :description => "The data bag the topologies are stored in"
37
+
38
+ option :overwrite,
39
+ :long => "--overwrite",
40
+ :description => "Whether to overwrite existing nodes",
41
+ :boolean => true
37
42
 
38
43
  # Make the base bootstrap options available on topo bootstrap
39
44
  self.options = (Chef::Knife::Bootstrap.options).merge(self.options)
@@ -65,50 +70,43 @@ class Chef
65
70
 
66
71
  # load and bootstrap each node that has a ssh_host
67
72
  nodes = merge_topo_properties(topo['nodes'], topo)
68
- @failed = []
69
- skipped = 0
70
- succeeded = 0
73
+
74
+ bootstrapped = []
75
+ skipped = []
76
+ existed = []
77
+ failed = []
78
+
71
79
  if nodes.length > 0
72
80
  nodes.each do |node_data|
73
- if node_data['ssh_host']
74
- run_bootstrap(node_data)
75
- succeeded += 1
81
+ node_name = node_data['name']
82
+ exists = resource_exists?("nodes/#{node_name}")
83
+ if(node_data['ssh_host'] && (config[:overwrite] || !exists))
84
+ if run_bootstrap(node_data, @bootstrap_args, exists)
85
+ bootstrapped << node_name
86
+ else
87
+ failed << node_name
88
+ end
76
89
  else
77
- ui.info "Node #{node_data['name']} does not have ssh_host specified - skipping bootstrap"
78
- skipped += 1
90
+ if(exists)
91
+ existed << node_name
92
+ else
93
+ skipped << node_name
94
+ end
79
95
  end
80
-
81
96
  end
82
- ui.info "Bootstrapped #{nodes.length - (@failed.length + skipped)} nodes and skipped #{skipped} nodes of #{nodes.length} in topology #{display_name(topo)}"
83
- ui.warn "#{@failed.length} nodes [ #{@failed.join(', ')} ] failed to bootstrap" if @failed.length > 0
97
+ ui.info("Bootstrapped #{bootstrapped.length} nodes [ #{bootstrapped.join(', ')} ]")
98
+ ui.info("Skipped #{skipped.length} nodes [ #{skipped.join(', ')} ] because they had no ssh_host information") if skipped.length > 0
99
+ if existed.length > 0
100
+ ui.info("Skipped #{existed.length} nodes [ #{existed.join(', ')} ] because they already exist. " +
101
+ "Specify --overwrite to re-bootstrap existing nodes. " +
102
+ "If you are using Chef Vault, you may need to use --bootstrap-vault options in this case.")
103
+ end
104
+ ui.warn("#{failed.length} nodes [ #{failed.join(', ')} ] failed to bootstrap due to errors") if failed.length > 0
84
105
  else
85
106
  ui.info "No nodes found for topology #{display_name(topo)}"
86
107
  end
87
108
  end
88
109
 
89
- # Setup the bootstrap args and run the bootstrap command
90
- def run_bootstrap(node_data)
91
- node_name = node_data['name']
92
-
93
- args = @bootstrap_args
94
- args += ['-N', node_name] if(node_name)
95
- args += ['-E', node_data['chef_environment']] if(node_data['chef_environment'])
96
- args[1] = node_data['ssh_host']
97
- args += [ '--ssh-port', node_data['ssh_port']] if node_data['ssh_port']
98
- args += [ '--run-list' , node_data['run_list'].join(',')] if node_data['run_list']
99
- args += [ '--json-attributes' , node_data['normal'].to_json] if node_data['normal']
100
-
101
- ui.info "Bootstrapping node #{node_name}"
102
- begin
103
- run_cmd(Chef::Knife::Bootstrap, args)
104
- rescue Exception => e
105
- raise if Chef::Config[:verbosity] == 2
106
- @failed << node_name
107
- ui.warn "bootstrap of node #{node_name} exited with error"
108
- humanize_exception(e)
109
- end
110
- end
111
-
112
110
  include Chef::Knife::TopologyHelper
113
111
 
114
112
  end
@@ -151,7 +151,7 @@ class Chef
151
151
 
152
152
  # Print out qualified attributes
153
153
  def print_qualified_attr(file, qualifier_hash)
154
- file.puts "if node['topo']['#{qualifier_hash['qualifier']}'] == \"#{qualifier_hash['value']}\""
154
+ file.puts "if node['topo'] && node['topo']['#{qualifier_hash['qualifier']}'] == \"#{qualifier_hash['value']}\""
155
155
  print_priority_attrs(file, qualifier_hash, 2)
156
156
  file.puts "end"
157
157
  end
@@ -27,7 +27,7 @@ class Chef
27
27
 
28
28
  deps do
29
29
  Chef::Knife::TopoCookbookUpload.load_deps
30
- Chef::Knife::TopoBootstrap.load_deps
30
+ Chef::Knife::Bootstrap.load_deps
31
31
  end
32
32
 
33
33
  banner "knife topo create TOPOLOGY (options)"
@@ -46,19 +46,24 @@ class Chef
46
46
  :long => "--disable-upload",
47
47
  :description => "Do not upload topo cookbooks",
48
48
  :boolean => true
49
+
50
+ option :overwrite,
51
+ :long => "--overwrite",
52
+ :description => "Whether to overwrite existing nodes",
53
+ :boolean => true
49
54
 
50
55
  # Make called command options available
51
56
  opts = self.options
52
- self.options = (Chef::Knife::TopoBootstrap.options).merge(Chef::Knife::TopoCookbookUpload.options)
57
+ self.options = (Chef::Knife::Bootstrap.options).merge(Chef::Knife::TopoCookbookUpload.options)
53
58
  self.options.merge!(opts)
54
59
 
55
60
  def initialize (args)
56
61
  super
57
- @topo_bootstrap_args = initialize_cmd_args(args, [ 'topo', 'bootstrap', @name_args[0] ])
62
+ @bootstrap_args = initialize_cmd_args(args, [ 'bootstrap', '' ])
58
63
  @topo_upload_args = initialize_cmd_args(args, [ 'topo', 'cookbook', 'upload', @name_args[0] ])
59
64
 
60
65
  # All called commands need to accept union of options
61
- Chef::Knife::TopoBootstrap.options = options
66
+ Chef::Knife::Bootstrap.options = options
62
67
  Chef::Knife::TopoCookbookUpload.options = options
63
68
  end
64
69
 
@@ -99,20 +104,55 @@ class Chef
99
104
  nodes = merge_topo_properties(topo_hash['nodes'], topo_hash)
100
105
  config[:disable_editing] = true
101
106
 
107
+ bootstrapped = []
108
+ updated = []
109
+ skipped = []
110
+ failed = []
111
+
102
112
  if nodes && nodes.length > 0
103
- nodes.each do |updates|
104
- node_name = updates['name']
105
- node = update_node(updates)
113
+
114
+ nodes.each do |node_data|
115
+ node_name = node_data['name']
116
+
117
+ exists = resource_exists?("nodes/#{node_name}")
118
+ if(node_data['ssh_host'] && config[:bootstrap] && (config[:overwrite] || !exists))
119
+ if run_bootstrap(node_data, @bootstrap_args, exists)
120
+ bootstrapped << node_name
121
+ else
122
+ failed << node_name
123
+ end
124
+ else
125
+ if(exists)
126
+ updated << node_name
127
+ node = update_node(node_data)
128
+ else
129
+ skipped << node_name
130
+ end
131
+ end
132
+ end
133
+
134
+ ui.info("Topology #{display_name(topo_hash)} created, containing #{nodes.length} nodes")
135
+ ui.info("Build information: " + topo_hash['buildstamp']) if topo_hash['buildstamp']
136
+
137
+ if(config[:bootstrap])
138
+ ui.info("Bootstrapped #{bootstrapped.length} nodes [ #{bootstrapped.join(', ')} ]")
139
+ if updated.length > 0
140
+ ui.info("Updated #{updated.length} nodes [ #{updated.join(', ')} ] because they already exist. " +
141
+ "Specify --overwrite to re-bootstrap existing nodes. " +
142
+ "If you are using Chef Vault, you may need to use --bootstrap-vault options in this case.")
143
+ end
144
+ ui.info("Skipped #{skipped.length} nodes [ #{skipped.join(', ')} ] because they had no ssh_host information") if skipped.length > 0
145
+ else
146
+ ui.info("Updated #{updated.length} nodes [ #{updated.join(', ')} ]")
147
+ ui.info("Skipped #{skipped.length} nodes [ #{skipped.join(', ')} ] because they do not exist") if skipped.length > 0
106
148
  end
107
- # if bootstrap is specified, run the bootstrap command
108
- run_cmd(Chef::Knife::TopoBootstrap, @topo_bootstrap_args) if config[:bootstrap]
149
+
150
+ ui.warn("#{failed.length} nodes [ #{failed.join(', ')} ] failed to bootstrap due to errors") if failed.length > 0
151
+
109
152
  else
110
153
  ui.info "No nodes found for topology #{display_name(topo_hash)}"
111
154
  end
112
-
113
- ui.info("Topology #{display_name(topo_hash)} created")
114
- ui.info("Build information: " + topo_hash['buildstamp']) if topo_hash['buildstamp']
115
-
155
+
116
156
  end
117
157
 
118
158
  include Chef::Knife::TopologyHelper
@@ -75,8 +75,8 @@ class Chef
75
75
  # load then update and save the node
76
76
  node = Chef::Node.load(node_name)
77
77
 
78
- if node.normal['topo'] && node.normal['topo']['name'] == @topo_name
79
- node.normal['topo'].delete('name')
78
+ if node['topo'] && node['topo']['name'] == @topo_name
79
+ node.rm('topo','name')
80
80
  ui.info "Removing node #{node.name} from topology"
81
81
  node.save
82
82
  end
@@ -37,8 +37,12 @@ class Chef
37
37
 
38
38
  def run
39
39
 
40
- topo_bag = topo_bag_name(config[:data_bag])
41
- output(format_list_for_display(Chef::DataBag.load(topo_bag)))
40
+ begin
41
+ topo_bag = topo_bag_name(config[:data_bag])
42
+ output(format_list_for_display(Chef::DataBag.load(topo_bag)))
43
+ rescue Net::HTTPServerException => e
44
+ raise unless e.to_s =~ /^404/
45
+ end
42
46
 
43
47
  end
44
48
 
@@ -21,6 +21,7 @@ require 'chef/node'
21
21
  require 'chef/encrypted_data_bag_item'
22
22
  require 'chef/environment'
23
23
  require 'chef/knife/core/object_loader'
24
+ require 'chef/rest'
24
25
 
25
26
  class Chef
26
27
  class Knife
@@ -104,7 +105,8 @@ class Chef
104
105
  merged_nodes = nodes ? nodes.clone : []
105
106
  merged_nodes.each do |nodeprops|
106
107
 
107
- normal_defaults = topo_hash['normal'] ? topo_hash['normal'].clone : {}
108
+ normal_defaults = topo_hash['normal'] ?
109
+ Marshal.load(Marshal.dump(topo_hash['normal'])) : {}
108
110
  nodeprops['normal'] ||= {}
109
111
  nodeprops['normal'] = prop_merge!(normal_defaults, nodeprops['normal'])
110
112
  nodeprops['normal'] = prop_merge!(nodeprops['normal'], nodeprops['attributes']) if nodeprops['attributes']
@@ -309,6 +311,55 @@ class Chef
309
311
 
310
312
  version
311
313
  end
314
+
315
+ # check if resource exists
316
+ def resource_exists?(relative_path)
317
+ rest.get_rest(relative_path)
318
+ true
319
+ rescue Net::HTTPServerException => e
320
+ raise unless e.response.code == "404"
321
+ false
322
+ end
323
+
324
+ # Setup the bootstrap args and run the bootstrap command
325
+ def run_bootstrap(node_data, bootstrap_args, overwrite=false)
326
+ node_name = node_data['name']
327
+
328
+ args = bootstrap_args
329
+
330
+ # We need to remove the --bootstrap option, if it exists, because its not valid for knife bootstrap
331
+ args -= ['--bootstrap']
332
+
333
+ # And set up the node-specific data
334
+ args += ['-N', node_name] if(node_name)
335
+ args += ['-E', node_data['chef_environment']] if(node_data['chef_environment'])
336
+ args[1] = node_data['ssh_host']
337
+ args += [ '--ssh-port', node_data['ssh_port']] if node_data['ssh_port']
338
+ args += [ '--run-list' , node_data['run_list'].join(',')] if node_data['run_list']
339
+ args += [ '--json-attributes' , node_data['normal'].to_json] if node_data['normal']
340
+
341
+ if overwrite
342
+ ui.info("Node #{node_name} exists and will be overwritten")
343
+ # delete node first so vault refresh does not pick up existing node
344
+ begin
345
+ rest.delete("nodes/#{node_name}")
346
+ rest.delete("clients/#{node_name}")
347
+ rescue Net::HTTPServerException => e
348
+ raise unless e.response.code == "404"
349
+ end
350
+ end
351
+
352
+ ui.info "Bootstrapping node #{node_name}"
353
+ begin
354
+ run_cmd(Chef::Knife::Bootstrap, args)
355
+ true
356
+ rescue Exception => e
357
+ raise if Chef::Config[:verbosity] == 2
358
+ ui.warn "bootstrap of node #{node_name} exited with error"
359
+ humanize_exception(e)
360
+ false
361
+ end
362
+ end
312
363
 
313
364
  end
314
365
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: knife-topo
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christine Draper
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-07-24 00:00:00.000000000 Z
11
+ date: 2015-12-15 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Knife-topo uses a JSON file to capture a topology of nodes, which can
14
14
  be loaded into Chef and bootstrapped
@@ -53,7 +53,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
53
53
  version: '0'
54
54
  requirements: []
55
55
  rubyforge_project:
56
- rubygems_version: 2.4.4
56
+ rubygems_version: 2.4.8
57
57
  signing_key:
58
58
  specification_version: 4
59
59
  summary: Knife plugin to manage topologies of nodes