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.
- checksums.yaml +4 -4
- data/README.md +125 -148
- data/knife-topo.gemspec +12 -12
- data/lib/chef/knife/topo/bootstrap_helper.rb +75 -0
- data/lib/chef/knife/topo/command_helper.rb +71 -0
- data/lib/chef/knife/topo/consts.rb +4 -0
- data/lib/chef/knife/topo/loader.rb +137 -0
- data/lib/chef/knife/topo/node_update_helper.rb +111 -0
- data/lib/chef/knife/topo/processor.rb +76 -0
- data/lib/chef/knife/topo/processor/via_cookbook.rb +118 -0
- data/lib/chef/knife/topo/processor/via_cookbook_print.rb +97 -0
- data/lib/chef/knife/topo/version.rb +3 -4
- data/lib/chef/knife/topo_bootstrap.rb +107 -79
- data/lib/chef/knife/topo_cookbook_create.rb +41 -144
- data/lib/chef/knife/topo_cookbook_upload.rb +40 -54
- data/lib/chef/knife/topo_create.rb +120 -132
- data/lib/chef/knife/topo_delete.rb +67 -65
- data/lib/chef/knife/topo_export.rb +108 -133
- data/lib/chef/knife/topo_import.rb +62 -76
- data/lib/chef/knife/topo_list.rb +18 -25
- data/lib/chef/knife/topo_search.rb +61 -61
- data/lib/chef/knife/topo_update.rb +25 -110
- data/lib/chef/topo/converter.rb +75 -0
- data/lib/chef/topo/converter/topo_v1.rb +123 -0
- data/lib/chef/topology.rb +137 -0
- metadata +13 -3
- data/lib/chef/knife/topology_helper.rb +0 -366
@@ -0,0 +1,118 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Christine Draper (<christine_draper@thirdwaveinsights.com>)
|
3
|
+
# Copyright:: Copyright (c) 2015 ThirdWave Insights LLC
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
#
|
20
|
+
# Converts data in a given format into V2 topo JSON format
|
21
|
+
#
|
22
|
+
require 'chef/knife/topo/processor'
|
23
|
+
require 'chef/knife/topo/processor/via_cookbook_print'
|
24
|
+
require 'chef/knife/topo/command_helper'
|
25
|
+
require 'chef/knife/cookbook_upload' unless defined? Chef::Knife::CookbookUpload
|
26
|
+
require 'chef/knife/cookbook_create'
|
27
|
+
|
28
|
+
module KnifeTopo
|
29
|
+
# Class to provide context to execute knife topo helper methods
|
30
|
+
class KnifeHelper < Chef::Knife
|
31
|
+
include KnifeTopo::CommandHelper
|
32
|
+
end
|
33
|
+
|
34
|
+
# Process attributes via a cookbook
|
35
|
+
class ViaCookbookProcessor < KnifeTopo::Processor
|
36
|
+
attr_accessor :cookbook, :filename
|
37
|
+
|
38
|
+
include KnifeTopo::ViaCookbookPrint
|
39
|
+
|
40
|
+
def initialize(topo)
|
41
|
+
super
|
42
|
+
data = @topo['strategy_data'] || {}
|
43
|
+
@cookbook = data['cookbook'] || topo.topo_name
|
44
|
+
@filename = data['filename'] || 'topology'
|
45
|
+
@helper = KnifeHelper.new
|
46
|
+
end
|
47
|
+
|
48
|
+
register_processor('via_cookbook', name)
|
49
|
+
|
50
|
+
def generate_nodes
|
51
|
+
super
|
52
|
+
end
|
53
|
+
|
54
|
+
# generate attributes to cookbook
|
55
|
+
# context['cmd'] must be calling command
|
56
|
+
# context['cmd_args'] must be calling command's args
|
57
|
+
def generate_artifacts(context = {})
|
58
|
+
@cmd = context['cmd']
|
59
|
+
@cmd_args = context['cmd_args'] || []
|
60
|
+
@config = Chef::Config.merge!(@cmd.config)
|
61
|
+
return unless @cmd && cookbook_path
|
62
|
+
run_create_cookbook
|
63
|
+
create_attr_file(
|
64
|
+
cookbook_path,
|
65
|
+
cookbook_contents
|
66
|
+
)
|
67
|
+
end
|
68
|
+
|
69
|
+
def run_create_cookbook
|
70
|
+
create_args = @helper.initialize_cmd_args(
|
71
|
+
@cmd_args, @cmd.name_args, %w(cookbook create)
|
72
|
+
)
|
73
|
+
create_args[2] = @cookbook
|
74
|
+
# set options from calling command, so validation does not fail
|
75
|
+
Chef::Knife::CookbookCreate.options = @cmd.class.options
|
76
|
+
@helper.run_cmd(Chef::Knife::CookbookCreate, create_args)
|
77
|
+
rescue StandardError => e
|
78
|
+
raise if Chef::Config[:verbosity] == 2
|
79
|
+
@helper.ui.warn "Create of cookbook #{@cookbook} exited with error"
|
80
|
+
@helper.humanize_exception(e)
|
81
|
+
end
|
82
|
+
|
83
|
+
def create_attr_file(dir, contents)
|
84
|
+
@helper.ui.info("** Creating attribute file: #{@filename}")
|
85
|
+
|
86
|
+
name = @filename << '.rb' unless File.extname(@filename) == '.rb'
|
87
|
+
filepath = File.join(dir, @cookbook, 'attributes', name)
|
88
|
+
File.open(filepath, 'w') { |file| file.write(contents) }
|
89
|
+
end
|
90
|
+
|
91
|
+
def cookbook_path
|
92
|
+
paths = @config['cookbook_path']
|
93
|
+
return unless paths
|
94
|
+
paths.first
|
95
|
+
end
|
96
|
+
|
97
|
+
def upload_artifacts(context = {})
|
98
|
+
@cmd = context['cmd']
|
99
|
+
@cmd_args = context['cmd_args'] || []
|
100
|
+
return unless @cmd && !@cmd.config[:disable_upload]
|
101
|
+
run_upload_cookbook
|
102
|
+
end
|
103
|
+
|
104
|
+
def run_upload_cookbook
|
105
|
+
upload_args = @helper.initialize_cmd_args(
|
106
|
+
@cmd_args, @cmd.name_args, %w(cookbook upload)
|
107
|
+
)
|
108
|
+
upload_args[2] = @cookbook
|
109
|
+
# set options from calling command, so validation does not fail
|
110
|
+
Chef::Knife::CookbookUpload.options = @cmd.class.options
|
111
|
+
@helper.run_cmd(Chef::Knife::CookbookUpload, upload_args)
|
112
|
+
rescue StandardError => e
|
113
|
+
raise if Chef::Config[:verbosity] == 2
|
114
|
+
@helper.ui.warn "Upload of cookbook #{@cookbook} exited with error"
|
115
|
+
@helper.humanize_exception(e)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Christine Draper (<christine_draper@thirdwaveinsights.com>)
|
3
|
+
# Copyright:: Copyright (c) 2015 ThirdWave Insights LLC
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
require 'chef/knife/topo/consts'
|
19
|
+
|
20
|
+
module KnifeTopo
|
21
|
+
# Prints attribute file contents to string
|
22
|
+
module ViaCookbookPrint
|
23
|
+
def print_attr_header(contents, cookbook_name, filename)
|
24
|
+
copyright = @config['cookbook_copyright']
|
25
|
+
contents << "
|
26
|
+
#
|
27
|
+
# THIS FILE IS GENERATED BY KNIFE TOPO - MANUAL CHANGES WILL BE OVERWRITTEN
|
28
|
+
#
|
29
|
+
# Cookbook Name:: #{cookbook_name}
|
30
|
+
# Attribute File:: #{filename}
|
31
|
+
#
|
32
|
+
"
|
33
|
+
return unless copyright
|
34
|
+
contents << "
|
35
|
+
# Copyright (c) #{Time.now.year} #{copyright}
|
36
|
+
#
|
37
|
+
"
|
38
|
+
end
|
39
|
+
|
40
|
+
def print_attr(contents, lhs, value1)
|
41
|
+
if value1.is_a?(Hash)
|
42
|
+
value1.each do |key, value2|
|
43
|
+
print_attr(contents, "#{lhs}['#{key}']", value2)
|
44
|
+
end
|
45
|
+
else
|
46
|
+
ruby_str = value1.nil? ? 'nil' : Chef::JSONCompat.to_json(value1)
|
47
|
+
contents << "#{lhs} = #{ruby_str}\n"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def print_attrs(contents, attrs, indent = 0)
|
52
|
+
return unless attrs
|
53
|
+
KnifeTopo::PRIORITIES.each do |priority|
|
54
|
+
next unless attrs[priority]
|
55
|
+
lhs = ''
|
56
|
+
indent.times { lhs << ' ' }
|
57
|
+
lhs << priority
|
58
|
+
print_attr(contents, lhs, attrs[priority])
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def print_attrs_for_node(contents, n)
|
63
|
+
if n['node_type']
|
64
|
+
print_node_type_attr_header(contents, n['node_type'])
|
65
|
+
else
|
66
|
+
print_node_name_attr_header(contents, n['name'])
|
67
|
+
end
|
68
|
+
print_attrs(contents, n, 2)
|
69
|
+
contents << "end\n"
|
70
|
+
end
|
71
|
+
|
72
|
+
def print_node_type_attr_header(contents, node_type)
|
73
|
+
contents << "# Attributes for node type #{node_type}\n"
|
74
|
+
contents << "if node['topo'] && node['topo']['node_type']" \
|
75
|
+
" == '#{node_type}'\n"
|
76
|
+
end
|
77
|
+
|
78
|
+
def print_node_name_attr_header(contents, node_name)
|
79
|
+
contents << "# Attributes for node name #{node_name}\n"
|
80
|
+
contents << "if node.name == '#{node_name}'\n"
|
81
|
+
end
|
82
|
+
|
83
|
+
def cookbook_contents
|
84
|
+
contents = ''
|
85
|
+
print_attr_header(contents, @cookbook, @filename)
|
86
|
+
print_attrs(contents, @topo['attributes'])
|
87
|
+
@topo.nodes.each do |n|
|
88
|
+
print_attrs_for_node(contents, n)
|
89
|
+
end
|
90
|
+
contents
|
91
|
+
end
|
92
|
+
|
93
|
+
def copyright
|
94
|
+
Chef::Config.copyright || @cmd.config['copyright']
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -16,99 +16,127 @@
|
|
16
16
|
# limitations under the License.
|
17
17
|
#
|
18
18
|
|
19
|
-
|
20
|
-
|
19
|
+
require 'chef/knife/topo/bootstrap_helper'
|
20
|
+
require 'chef/knife/topo/loader'
|
21
21
|
require 'chef/knife/bootstrap'
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
|
-
banner "knife topo bootstrap TOPOLOGY (options)"
|
32
|
-
|
33
|
-
option :data_bag,
|
34
|
-
:short => '-D DATA_BAG',
|
35
|
-
:long => "--data-bag DATA_BAG",
|
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
|
23
|
+
module KnifeTopo
|
24
|
+
# knife topo bootstrap
|
25
|
+
class TopoBootstrap < Chef::Knife
|
26
|
+
deps do
|
27
|
+
require 'chef/knife/topo/processor'
|
28
|
+
end
|
42
29
|
|
43
|
-
|
44
|
-
|
30
|
+
include KnifeTopo::BootstrapHelper
|
31
|
+
include KnifeTopo::Loader
|
32
|
+
|
33
|
+
banner 'knife topo bootstrap TOPOLOGY (options)'
|
34
|
+
|
35
|
+
option(
|
36
|
+
:data_bag,
|
37
|
+
short: '-D DATA_BAG',
|
38
|
+
long: '--data-bag DATA_BAG',
|
39
|
+
description: 'The data bag the topologies are stored in'
|
40
|
+
)
|
41
|
+
|
42
|
+
option(
|
43
|
+
:overwrite,
|
44
|
+
long: '--overwrite',
|
45
|
+
description: 'Whether to overwrite existing nodes',
|
46
|
+
boolean: true
|
47
|
+
)
|
48
|
+
|
49
|
+
# Make the base bootstrap options available on topo bootstrap
|
50
|
+
self.options = (Chef::Knife::Bootstrap.options).merge(TopoBootstrap.options)
|
51
|
+
|
52
|
+
attr_accessor :msgs, :results
|
53
|
+
|
54
|
+
def bootstrap_msgs
|
55
|
+
{
|
56
|
+
bootstrapped: 'Bootstrapped %{num} nodes [ %{list} ]',
|
57
|
+
skipped: 'Unexpected error',
|
58
|
+
skipped_ssh: 'Did not bootstrap %{num} nodes [ %{list} ] ' \
|
59
|
+
'because they do not have an ssh_host',
|
60
|
+
existed: 'Did not bootstrap %{num} nodes [ %{list} ] because '\
|
61
|
+
"they already exist.\n"\
|
62
|
+
"Specify --overwrite to re-bootstrap existing nodes. \n",
|
63
|
+
failed: '%{num} nodes [ %{list} ] failed to bootstrap due to errors'
|
64
|
+
}
|
65
|
+
end
|
45
66
|
|
46
|
-
|
47
|
-
|
48
|
-
|
67
|
+
def initialize(args)
|
68
|
+
super
|
69
|
+
@bootstrap_args = initialize_cmd_args(args, name_args, ['bootstrap', ''])
|
70
|
+
@results = {
|
71
|
+
bootstrapped: [], skipped: [], skipped_ssh: [], existed: [], failed: []
|
72
|
+
}
|
73
|
+
@msgs = bootstrap_msgs
|
74
|
+
@bootstrap = true
|
75
|
+
|
76
|
+
# All called commands need to accept union of options
|
77
|
+
Chef::Knife::Bootstrap.options = options
|
78
|
+
end
|
49
79
|
|
50
|
-
|
51
|
-
|
80
|
+
def run
|
81
|
+
validate_args
|
52
82
|
|
83
|
+
# load and bootstrap each node that has a ssh_host
|
84
|
+
@topo = load_topo_from_server_or_exit(@topo_name)
|
85
|
+
@processor = KnifeTopo::Processor.for_topo(@topo)
|
86
|
+
nodes = @processor.generate_nodes
|
87
|
+
nodes.each do |node_data|
|
88
|
+
node_bootstrap(node_data)
|
53
89
|
end
|
54
90
|
|
55
|
-
|
56
|
-
|
57
|
-
show_usage
|
58
|
-
ui.fatal("You must specify the name of a topology")
|
59
|
-
exit 1
|
60
|
-
end
|
61
|
-
|
62
|
-
@bag_name = topo_bag_name(config[:data_bag])
|
63
|
-
@topo_name = @name_args[0]
|
91
|
+
report
|
92
|
+
end
|
64
93
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
94
|
+
def validate_args
|
95
|
+
unless @name_args[0]
|
96
|
+
show_usage
|
97
|
+
ui.fatal('You must specify the name of a topology')
|
98
|
+
exit 1
|
99
|
+
end
|
100
|
+
@topo_name = @name_args[0]
|
101
|
+
end
|
70
102
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
nodes.each do |node_data|
|
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
|
89
|
-
else
|
90
|
-
if(exists)
|
91
|
-
existed << node_name
|
92
|
-
else
|
93
|
-
skipped << node_name
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
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
|
103
|
+
# rubocop:disable Metrics/MethodLength
|
104
|
+
def node_bootstrap(node_data)
|
105
|
+
node_name = node_data['name']
|
106
|
+
state = :skipped_ssh
|
107
|
+
if node_data['ssh_host']
|
108
|
+
exists = resource_exists?("nodes/#{node_name}")
|
109
|
+
if config[:overwrite] || !exists
|
110
|
+
success = run_bootstrap(node_data, @bootstrap_args, exists)
|
111
|
+
state = success ? :bootstrapped : :failed
|
105
112
|
else
|
106
|
-
|
113
|
+
state = :existed
|
107
114
|
end
|
108
115
|
end
|
116
|
+
@results[state] << node_name
|
117
|
+
success
|
118
|
+
end
|
119
|
+
# rubocop:enable Metrics/MethodLength
|
120
|
+
|
121
|
+
# Report is used by create, update and bootstrap commands
|
122
|
+
def report
|
123
|
+
if @topo['nodes'].length > 0
|
124
|
+
report_msg(:bootstrapped, :info, false) if @bootstrap
|
125
|
+
report_msg(:skipped, :info, true)
|
126
|
+
report_msg(:skipped_ssh, :info, true)
|
127
|
+
report_msg(:existed, :info, true)
|
128
|
+
report_msg(:failed, :warn, true) if @bootstrap
|
129
|
+
else
|
130
|
+
ui.info 'No nodes found'
|
131
|
+
end
|
132
|
+
ui.info("Topology: #{@topo.display_info}")
|
133
|
+
end
|
109
134
|
|
110
|
-
|
111
|
-
|
135
|
+
def report_msg(state, level, only_non_zero = true)
|
136
|
+
nodes = @results[state]
|
137
|
+
return if only_non_zero && nodes.length == 0
|
138
|
+
ui.send(level, @msgs[state] %
|
139
|
+
{ num: nodes.length, list: nodes.join(', ') })
|
112
140
|
end
|
113
141
|
end
|
114
142
|
end
|
@@ -16,161 +16,58 @@
|
|
16
16
|
# limitations under the License.
|
17
17
|
#
|
18
18
|
|
19
|
-
|
19
|
+
require 'chef/knife/topo/loader'
|
20
20
|
require 'chef/knife/cookbook_create'
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
end
|
29
|
-
|
30
|
-
banner "knife topo cookbook create TOPOLOGY [ TOPOLOGY_FILE ] (options)"
|
31
|
-
|
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
|
-
|
37
|
-
# Make the base cookbook create options available on topo cookbook
|
38
|
-
self.options = (Chef::Knife::CookbookCreate.options).merge(self.options)
|
39
|
-
|
40
|
-
def initialize (args)
|
41
|
-
super
|
42
|
-
@cookbook_create_args = initialize_cmd_args(args, [ 'cookbook', 'create' ])
|
43
|
-
|
44
|
-
# All called commands need to accept union of options
|
45
|
-
Chef::Knife::CookbookCreate.options = options
|
46
|
-
|
47
|
-
end
|
22
|
+
module KnifeTopo
|
23
|
+
# knife topo cookbook create
|
24
|
+
class TopoCookbookCreate < Chef::Knife
|
25
|
+
deps do
|
26
|
+
require 'chef/knife/topo/processor'
|
27
|
+
end
|
48
28
|
|
49
|
-
|
50
|
-
if !@name_args[0]
|
51
|
-
show_usage
|
52
|
-
ui.fatal("You must specify the name of a topology")
|
53
|
-
exit 1
|
54
|
-
end
|
55
|
-
|
56
|
-
@bag_name = topo_bag_name(config[:data_bag])
|
57
|
-
@topo_name = @name_args[0]
|
58
|
-
@topo_file = @name_args[1]
|
29
|
+
banner 'knife topo cookbook create TOPOLOGY_FILE (options)'
|
59
30
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
exit(1)
|
67
|
-
end
|
68
|
-
topo = topologies[index]
|
69
|
-
else
|
70
|
-
unless topo = load_from_server(@bag_name, @topo_name )
|
71
|
-
ui.fatal("Topology #{@bag_name}/#{@topo_name} does not exist on the server - use 'knife topo create' first")
|
72
|
-
exit(1)
|
73
|
-
end
|
74
|
-
end
|
31
|
+
option(
|
32
|
+
:data_bag,
|
33
|
+
short: '-D DATA_BAG',
|
34
|
+
long: '--data-bag DATA_BAG',
|
35
|
+
description: 'The data bag the topologies are stored in'
|
36
|
+
)
|
75
37
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
if attribute_cookbooks && attribute_cookbooks.length > 0
|
80
|
-
attribute_cookbooks.each do |cookbook_spec|
|
81
|
-
cookbook_name = cookbook_spec['cookbook']
|
82
|
-
run_create_cookbook(cookbook_name) unless cookbook_names.include?(cookbook_name)
|
83
|
-
cookbook_names << cookbook_name
|
84
|
-
create_attr_file(@cookbook_path, cookbook_name,
|
85
|
-
cookbook_spec['filename'] + ".rb", cookbook_spec)
|
86
|
-
end
|
87
|
-
else
|
88
|
-
ui.info "No topology cookbook has been specified for topology #{@topo_name}"
|
89
|
-
end
|
90
|
-
end
|
38
|
+
# Make the base cookbook create options available on topo cookbook
|
39
|
+
self.options = Chef::Knife::CookbookCreate.options.merge(
|
40
|
+
TopoCookbookCreate.options)
|
91
41
|
|
92
|
-
|
42
|
+
include KnifeTopo::Loader
|
93
43
|
|
94
|
-
|
44
|
+
def initialize(args)
|
45
|
+
super
|
46
|
+
@args = args
|
47
|
+
end
|
95
48
|
|
96
|
-
|
97
|
-
|
98
|
-
begin
|
99
|
-
command = run_cmd(Chef::Knife::CookbookCreate, @cookbook_create_args)
|
100
|
-
rescue Exception => e
|
101
|
-
raise if Chef::Config[:verbosity] == 2
|
102
|
-
ui.warn "Create of cookbook #{cookbook_name} exited with error"
|
103
|
-
humanize_exception(e)
|
104
|
-
end
|
49
|
+
def run
|
50
|
+
validate_args
|
105
51
|
|
106
|
-
|
107
|
-
|
108
|
-
|
52
|
+
@topo = load_topo_from_file_or_exit(@topo_file)
|
53
|
+
@processor = KnifeTopo::Processor.for_topo(@topo)
|
54
|
+
do_create_artifacts
|
55
|
+
end
|
109
56
|
|
57
|
+
def validate_args
|
58
|
+
unless @name_args[0]
|
59
|
+
show_usage
|
60
|
+
ui.fatal('You must specify a topology JSON file')
|
61
|
+
exit 1
|
110
62
|
end
|
63
|
+
@topo_file = @name_args[0]
|
64
|
+
end
|
111
65
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
file.puts <<-EOH
|
118
|
-
#
|
119
|
-
# THIS FILE IS GENERATED BY THE KNIFE TOPO PLUGIN - MANUAL CHANGES WILL BE OVERWRITTEN
|
120
|
-
#
|
121
|
-
# Cookbook Name:: #{cookbook_name}
|
122
|
-
# Attribute File:: #{filename}
|
123
|
-
#
|
124
|
-
# Copyright #{Time.now.year}, #{@copyright}
|
125
|
-
#
|
126
|
-
EOH
|
127
|
-
|
128
|
-
# Print out attribute line
|
129
|
-
def print_attr(file, lhs, value1)
|
130
|
-
if value1.is_a?(Hash)
|
131
|
-
value1.each do |key, value2|
|
132
|
-
print_attr(file, "#{lhs}['#{key}']", value2)
|
133
|
-
end
|
134
|
-
else
|
135
|
-
rubyString = (value1 == nil) ? "nil" : Chef::JSONCompat.to_json(value1);
|
136
|
-
file.write "#{lhs} = " + rubyString + " \n"
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
# Print out attributes hashed by priority
|
141
|
-
def print_priority_attrs(file, attrs, indent=0)
|
142
|
-
%w(default force_default normal override force_override).each do |priority|
|
143
|
-
if attrs[priority]
|
144
|
-
lhs = ""
|
145
|
-
indent.times { |i| lhs += " " }
|
146
|
-
lhs += priority
|
147
|
-
print_attr(file, lhs, attrs[priority])
|
148
|
-
end
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
# Print out qualified attributes
|
153
|
-
def print_qualified_attr(file, qualifier_hash)
|
154
|
-
file.puts "if node['topo'] && node['topo']['#{qualifier_hash['qualifier']}'] == \"#{qualifier_hash['value']}\""
|
155
|
-
print_priority_attrs(file, qualifier_hash, 2)
|
156
|
-
file.puts "end"
|
157
|
-
end
|
158
|
-
|
159
|
-
# Process the attributes not needing qualification
|
160
|
-
print_priority_attrs(file, attrs)
|
161
|
-
file.puts
|
162
|
-
|
163
|
-
# Process attributes that need to be qualified
|
164
|
-
if attrs['conditional']
|
165
|
-
attrs['conditional'].each do |qualified_attrs|
|
166
|
-
file.puts "# Attributes for specific #{qualified_attrs['qualifier']}"
|
167
|
-
print_qualified_attr(file, qualified_attrs)
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
66
|
+
def do_create_artifacts
|
67
|
+
@processor.generate_artifacts(
|
68
|
+
'cmd_args' => @args,
|
69
|
+
'cmd' => self
|
70
|
+
)
|
174
71
|
end
|
175
72
|
end
|
176
|
-
end
|
73
|
+
end
|