knife-topo 1.1.2 → 2.0.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.
@@ -17,71 +17,57 @@
17
17
  #
18
18
 
19
19
  require 'chef/knife'
20
- require_relative 'topology_helper'
20
+ require 'chef/knife/topo/loader'
21
+ require 'chef/knife/cookbook_upload' unless defined? Chef::Knife::CookbookUpload
21
22
 
22
- ## only require if not already defined (to prevent warning about already initialized constants)
23
- require 'chef/knife/cookbook_upload' if !defined? Chef::Knife::CookbookUpload
23
+ module KnifeTopo
24
+ # knife topo cookbook upload
25
+ class TopoCookbookUpload < Chef::Knife
26
+ deps do
27
+ require 'chef/knife/topo/processor'
28
+ end
24
29
 
25
- class Chef
26
- class Knife
27
- class TopoCookbookUpload < Chef::Knife
28
-
29
- deps do
30
- Chef::Knife::CookbookUpload.load_deps
31
- end
30
+ banner 'knife topo cookbook upload TOPOLOGY (options)'
32
31
 
33
- banner "knife topo cookbook upload [ TOPOLOGY ] (options)"
32
+ option(
33
+ :data_bag,
34
+ short: '-D DATA_BAG',
35
+ long: '--data-bag DATA_BAG',
36
+ description: 'The data bag the topologies are stored in'
37
+ )
34
38
 
35
- option :data_bag,
36
- :short => '-D DATA_BAG',
37
- :long => "--data-bag DATA_BAG",
38
- :description => "The data bag the topologies are stored in"
39
+ # Make called command options available
40
+ self.options = (Chef::Knife::CookbookUpload.options).merge(
41
+ TopoCookbookUpload.options)
39
42
 
40
- # Make called command options available
41
- self.options = (Chef::Knife::CookbookUpload.options).merge(self.options)
43
+ include KnifeTopo::Loader
42
44
 
43
- def initialize (args)
44
- super
45
- @topo_upload_args = initialize_cmd_args(args, [ 'cookbook', 'upload' ])
45
+ def initialize(args)
46
+ super
47
+ @args = args
46
48
 
47
- # All called commands need to accept union of options
48
- Chef::Knife::CookbookUpload.options = options
49
- end
50
-
51
- def run
52
- if !@name_args[0]
53
- show_usage
54
- ui.fatal("You must specify the name of a topology")
55
- exit 1
56
- end
49
+ # All called commands need to accept union of options
50
+ Chef::Knife::CookbookUpload.options = options
51
+ end
57
52
 
58
- bag_name = topo_bag_name(config[:data_bag])
59
- topo_name = @name_args[0]
53
+ def run
54
+ validate_args
60
55
 
61
- # Load the topology data
62
- unless topo = load_from_file(bag_name, topo_name )
63
- ui.fatal("Topology file #{topologies_path}/#{bag_name}/#{topo_name}.json not found - use 'knife topo import' first")
64
- exit(1)
65
- end
66
-
67
- # Run cookbook upload command on the topology cookbooks
68
- cookbook_names = []
69
- if topo['cookbook_attributes'] && topo['cookbook_attributes'].length > 0
70
- argPos = 2
71
- topo['cookbook_attributes'].each do |entry|
72
- cookbook_name = entry['cookbook']
73
- @topo_upload_args[argPos] = cookbook_name unless cookbook_names.include?(cookbook_name)
74
- cookbook_names << cookbook_name
75
- argPos += 1
76
- end
77
- run_cmd(Chef::Knife::CookbookUpload, @topo_upload_args)
78
- else
79
- ui.info("No cookbooks found for topology #{display_name(topo)}")
80
- end
81
- end
56
+ # Load the topology data
57
+ @topo = load_local_topo_or_exit(@topo_name)
82
58
 
83
- include Chef::Knife::TopologyHelper
59
+ # Run cookbook upload command on the topology cookbook
60
+ @processor = KnifeTopo::Processor.for_topo(@topo)
61
+ @processor.upload_artifacts('cmd' => self, 'cmd_args' => @args)
62
+ end
84
63
 
64
+ def validate_args
65
+ unless @name_args[0]
66
+ show_usage
67
+ ui.fatal('You must specify the name of a topology')
68
+ exit 1
69
+ end
70
+ @topo_name = @name_args[0]
85
71
  end
86
72
  end
87
73
  end
@@ -17,146 +17,134 @@
17
17
  #
18
18
 
19
19
  require 'chef/knife'
20
- require_relative 'topology_helper'
21
- require_relative 'topo_bootstrap'
22
- require_relative 'topo_cookbook_upload'
23
-
24
- class Chef
25
- class Knife
26
- class TopoCreate < Chef::Knife
27
-
28
- deps do
29
- Chef::Knife::TopoCookbookUpload.load_deps
30
- Chef::Knife::Bootstrap.load_deps
31
- end
20
+ require 'chef/knife/topo_bootstrap'
21
+ require 'chef/knife/cookbook_upload' unless defined? Chef::Knife::CookbookUpload
22
+ require 'chef/knife/topo/loader'
23
+ require 'chef/knife/topo/command_helper'
24
+ require 'chef/knife/topo/node_update_helper'
32
25
 
33
- banner "knife topo create TOPOLOGY (options)"
34
-
35
- option :data_bag,
36
- :short => '-D DATA_BAG',
37
- :long => "--data-bag DATA_BAG",
38
- :description => "The data bag the topologies are stored in"
39
-
40
- option :bootstrap,
41
- :long => "--bootstrap",
42
- :description => "Whether to bootstrap newly created nodes",
43
- :boolean => true
44
-
45
- option :disable_upload,
46
- :long => "--disable-upload",
47
- :description => "Do not upload topo cookbooks",
48
- :boolean => true
49
-
50
- option :overwrite,
51
- :long => "--overwrite",
52
- :description => "Whether to overwrite existing nodes",
53
- :boolean => true
54
-
55
- # Make called command options available
56
- opts = self.options
57
- self.options = (Chef::Knife::Bootstrap.options).merge(Chef::Knife::TopoCookbookUpload.options)
58
- self.options.merge!(opts)
59
-
60
- def initialize (args)
61
- super
62
- @bootstrap_args = initialize_cmd_args(args, [ 'bootstrap', '' ])
63
- @topo_upload_args = initialize_cmd_args(args, [ 'topo', 'cookbook', 'upload', @name_args[0] ])
64
-
65
- # All called commands need to accept union of options
66
- Chef::Knife::Bootstrap.options = options
67
- Chef::Knife::TopoCookbookUpload.options = options
68
- end
69
-
70
- def run
71
- if !@name_args[0]
72
- show_usage
73
- ui.fatal("You must specify the name of a topology")
74
- exit 1
75
- end
26
+ module KnifeTopo
27
+ # knife topo create
28
+ class TopoCreate < KnifeTopo::TopoBootstrap
29
+ deps do
30
+ require 'chef/knife/topo/processor'
31
+ KnifeTopo::TopoBootstrap.load_deps
32
+ end
76
33
 
77
- bag_name = topo_bag_name(config[:data_bag])
78
- topo_name = @name_args[0]
34
+ banner 'knife topo create TOPOLOGY (options)'
79
35
 
80
- # Load the topology data & create the topology bag
81
- unless topo = load_from_file(bag_name, topo_name )
82
- ui.fatal("Topology file #{topologies_path}/#{bag_name}/#{topo_name}.json not found - use 'knife topo import' first")
83
- exit(1)
84
- end
85
- @data_bag = create_bag(bag_name)
86
-
87
- # Add topology item to the data bag on the server
88
- begin
89
- topo.create
90
- rescue Net::HTTPServerException => e
91
- raise unless e.to_s =~ /^409/
92
- msg = "Topology #{topo_name} already exists - do you want to update it"
93
- msg = msg + " to version " + format_topo_version(topo) if topo['version']
94
- ui.confirm(msg, true, false)
95
- topo.save
96
- end
36
+ option(
37
+ :bootstrap,
38
+ long: '--bootstrap',
39
+ description: 'Whether to bootstrap newly created nodes',
40
+ boolean: true
41
+ )
42
+
43
+ option(
44
+ :disable_upload,
45
+ long: '--disable-upload',
46
+ description: 'Do not upload topo cookbooks',
47
+ boolean: true
48
+ )
49
+
50
+ # Make called command options available
51
+ orig_opts = KnifeTopo::TopoCreate.options
52
+ upload_opts = Chef::Knife::CookbookUpload.options
53
+ merged_opts = (KnifeTopo::TopoBootstrap.options).merge(upload_opts)
54
+ self.options = merged_opts.merge(orig_opts)
55
+
56
+ include KnifeTopo::CommandHelper
57
+ include KnifeTopo::NodeUpdateHelper
58
+ include KnifeTopo::Loader
59
+
60
+ def initialize(args)
61
+ super
62
+ @args = args
63
+ end
64
+
65
+ def bootstrap_msgs
66
+ msgs = super.dup
67
+ msgs[:existed] = 'Updated but did not bootstrap %{num} existing nodes '\
68
+ "[ %{list} ].\n Specify --overwrite to re-bootstrap existing nodes. \n"
69
+ msgs
70
+ end
71
+
72
+ def non_bootstrap_msgs
73
+ {
74
+ existed: 'Applied updates (if any) to %{num} nodes [ %{list} ]',
75
+ skipped_ssh: 'Unexpected error skipped_ssh',
76
+ skipped: 'Skipped %{num} nodes [ %{list} ] because they do not exist',
77
+ bootstrapped: 'Unexpected error bootstrapped',
78
+ failed: 'Unexpected error failed'
79
+ }
80
+ end
81
+
82
+ def run
83
+ validate_args
84
+ create_or_update_topo
85
+
86
+ # make sure env and cookbooks are in place
87
+ check_chef_env(@topo['chef_environment'])
88
+ upload_artifacts unless config[:disable_upload]
89
+ update_nodes
97
90
 
98
- # make sure env and cookbooks are in place
99
- check_chef_env(topo['chef_environment']) if topo['chef_environment']
100
- upload_cookbooks(@topo_upload_args) if (!config[:disable_upload])
101
-
102
- # update any existing nodes
103
- topo_hash = topo.raw_data
104
- nodes = merge_topo_properties(topo_hash['nodes'], topo_hash)
105
- config[:disable_editing] = true
106
-
107
- bootstrapped = []
108
- updated = []
109
- skipped = []
110
- failed = []
111
-
112
- if nodes && nodes.length > 0
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
148
- end
149
-
150
- ui.warn("#{failed.length} nodes [ #{failed.join(', ')} ] failed to bootstrap due to errors") if failed.length > 0
151
-
91
+ report
92
+ end
93
+
94
+ def processor
95
+ @processor ||= KnifeTopo::Processor.for_topo(@topo)
96
+ end
97
+
98
+ def validate_args
99
+ super
100
+ @bootstrap = config[:bootstrap]
101
+ @msgs = @bootstrap ? bootstrap_msgs : non_bootstrap_msgs
102
+ config[:disable_editing] = true
103
+ end
104
+
105
+ def create_or_update_topo
106
+ # Load the topology data & create the topology bag
107
+ @topo = load_local_topo_or_exit(@topo_name)
108
+ create_topo_bag
109
+
110
+ # Add topology item to the data bag on the server
111
+ @topo.create
112
+ rescue Net::HTTPServerException => e
113
+ raise unless e.to_s =~ /^409/
114
+ confirm_and_update_topo
115
+ end
116
+
117
+ def update_nodes
118
+ nodes = processor.generate_nodes
119
+ nodes.each do |node_data|
120
+ bootstrap_or_update_node(node_data)
121
+ end
122
+ end
123
+
124
+ def confirm_and_update_topo
125
+ version = @topo.topo_version
126
+ to_version_str = " to version #{version}"
127
+ msg = "Topology #{@topo_name} already exists - do you want to " \
128
+ "update it#{to_version_str if version}"
129
+ ui.confirm(msg, true, false)
130
+ @topo.save
131
+ end
132
+
133
+ def bootstrap_or_update_node(node_data)
134
+ node_name = node_data['name']
135
+ if @bootstrap
136
+ update_node(node_data) unless node_bootstrap(node_data)
137
+ else
138
+ if update_node(node_data)
139
+ @results[:existed] << node_name
152
140
  else
153
- ui.info "No nodes found for topology #{display_name(topo_hash)}"
141
+ @results[:skipped] << node_name
154
142
  end
155
-
156
143
  end
144
+ end
157
145
 
158
- include Chef::Knife::TopologyHelper
159
-
146
+ def upload_artifacts
147
+ processor.upload_artifacts('cmd' => self, 'cmd_args' => @args)
160
148
  end
161
149
  end
162
150
  end
@@ -17,80 +17,82 @@
17
17
  #
18
18
 
19
19
  require 'chef/knife'
20
- require_relative 'topology_helper'
20
+ require 'chef/knife/topo/loader'
21
+ require 'chef/knife/topo/command_helper'
21
22
 
22
- class Chef
23
- class Knife
24
- class TopoDelete < Chef::Knife
23
+ module KnifeTopo
24
+ # knife topo delete
25
+ class TopoDelete < Chef::Knife
26
+ deps do
27
+ require 'chef/node'
28
+ end
29
+
30
+ banner 'knife topo delete TOPOLOGY (options)'
31
+
32
+ option(
33
+ :data_bag,
34
+ short: '-D DATA_BAG',
35
+ long: '--data-bag DATA_BAG',
36
+ description: 'The data bag the topologies are stored in'
37
+ )
25
38
 
26
- deps do
27
- require 'chef/data_bag'
39
+ include KnifeTopo::CommandHelper
40
+ include KnifeTopo::Loader
41
+
42
+ def run
43
+ validate_args
44
+ config[:disable_editing] = true
45
+
46
+ # remove each node
47
+ @topo = load_topo_from_server(@topo_name)
48
+ unless @topo
49
+ ui.info "Topology #{topo_bag_name}/#{@topo_name} does not " \
50
+ 'exist on server'
51
+ exit(0)
28
52
  end
29
53
 
30
- banner "knife topo delete TOPOLOGY (options)"
54
+ confirm_and_delete_nodes
55
+ delete_topo
56
+ end
31
57
 
32
- option :data_bag,
33
- :short => '-D DATA_BAG',
34
- :long => "--data-bag DATA_BAG",
35
- :description => "The data bag the topologies are stored in"
36
-
58
+ def validate_args
59
+ unless @name_args[0]
60
+ show_usage
61
+ ui.fatal('You must specify the name of a topology')
62
+ exit 1
63
+ end
64
+ @topo_name = @name_args[0]
65
+ end
37
66
 
38
- def run
67
+ def confirm_and_delete_nodes
68
+ confirm("Do you want to delete topology #{@topo_name} - " \
69
+ 'this does not delete nodes')
70
+ @topo['nodes'].each do |node|
71
+ remove_node_from_topology(node['name'])
72
+ end if @topo['nodes']
73
+ end
74
+
75
+ def delete_topo
76
+ @topo.destroy(topo_bag_name, @topo_name)
77
+ ui.info "Deleted topology #{@topo_name}"
78
+ end
39
79
 
40
- topo_bag = topo_bag_name(config[:data_bag])
41
- @topo_name = @name_args[0]
42
-
43
- if @name_args.length == 1
44
-
45
- # remove each node
46
-
47
- unless topo = load_from_server(topo_bag, @topo_name)
48
- ui.info "Topology #{topo_bag}/#{@topo_name} does not exist on server"
49
- exit(0)
50
- end
51
-
52
- confirm("Do you want to delete topology #{@topo_name} - this does not delete nodes")
53
-
54
- topo['nodes'].each do | node |
55
- remove_node_from_topology(node['name'])
56
- end if topo['nodes']
57
-
58
- # delete the data bag item
59
- topo.destroy(topo_bag, @topo_name)
80
+ # Remove the topo name attribute from all nodes, so topo search
81
+ # knows they are not in the topology
82
+ def remove_node_from_topology(node_name)
83
+ # load then update and save the node
84
+ node = Chef::Node.load(node_name)
60
85
 
61
- ui.info "Deleted topology #{@topo_name}"
62
- else
63
- show_usage
64
- ui.fatal("You must specify a topology name")
65
- exit 1
66
- end
67
- end
68
-
69
- # Remove the topo name attribute from all nodes, so topo search knows they are not in the topology
70
- def remove_node_from_topology(node_name)
71
-
72
- config[:disable_editing] = true
73
- begin
74
-
75
- # load then update and save the node
76
- node = Chef::Node.load(node_name)
77
-
78
- if node['topo'] && node['topo']['name'] == @topo_name
79
- node.rm('topo','name')
80
- ui.info "Removing node #{node.name} from topology"
81
- node.save
82
- end
83
-
84
- rescue Net::HTTPServerException => e
85
- raise unless e.to_s =~ /^404/
86
- # Node has not been created
87
- end
88
-
89
- return node
86
+ if node['topo'] && node['topo']['name'] == @topo_name
87
+ node.rm('topo', 'name')
88
+ ui.info "Removing node #{node.name} from topology"
89
+ node.save
90
90
  end
91
-
92
- include Chef::Knife::TopologyHelper
91
+ node
93
92
 
93
+ rescue Net::HTTPServerException => e
94
+ raise unless e.to_s =~ /^404/
95
+ # Node has not been created
94
96
  end
95
97
  end
96
- end
98
+ end