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
data/knife-topo.gemspec
CHANGED
@@ -4,17 +4,17 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'chef/knife/topo/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
8
|
-
spec.version =
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
11
|
-
spec.summary =
|
12
|
-
spec.description =
|
13
|
-
|
14
|
-
spec.
|
15
|
-
|
16
|
-
spec.files = Dir.glob("{lib}/**/*") +
|
17
|
-
['LICENSE', 'README.md', __FILE__]
|
18
|
-
spec.require_paths = ["lib"]
|
7
|
+
spec.name = 'knife-topo'
|
8
|
+
spec.version = KnifeTopo::VERSION
|
9
|
+
spec.authors = ['Christine Draper']
|
10
|
+
spec.email = ['christine_draper@thirdwaveinsights.com']
|
11
|
+
spec.summary = 'Knife plugin to manage topologies of nodes'
|
12
|
+
spec.description = 'Knife-topo uses a JSON file to capture a topology '\
|
13
|
+
'of nodes, which can be loaded into Chef and bootstrapped'
|
14
|
+
spec.homepage = 'https://github.com/christinedraper/knife-topo'
|
15
|
+
spec.license = 'Apache License (2.0)'
|
19
16
|
|
17
|
+
spec.files = Dir.glob('{lib}/**/*') +
|
18
|
+
['LICENSE', 'README.md', __FILE__]
|
19
|
+
spec.require_paths = ['lib']
|
20
20
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Christine Draper (<christine_draper@thirdwaveinsights.com>)
|
3
|
+
# Copyright:: Copyright (c) 2014 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
|
+
require 'chef/rest'
|
20
|
+
require 'chef/knife/topo/command_helper'
|
21
|
+
|
22
|
+
module KnifeTopo
|
23
|
+
# Node update helper for knife topo
|
24
|
+
module BootstrapHelper
|
25
|
+
include KnifeTopo::CommandHelper
|
26
|
+
|
27
|
+
# Setup the bootstrap args and run the bootstrap command
|
28
|
+
def run_bootstrap(data, bootstrap_args, overwrite = false)
|
29
|
+
node_name = data['name']
|
30
|
+
args = setup_bootstrap_args(bootstrap_args, data)
|
31
|
+
delete_client_node(node_name) if overwrite
|
32
|
+
|
33
|
+
ui.info "Bootstrapping node #{node_name}"
|
34
|
+
run_cmd(Chef::Knife::Bootstrap, args)
|
35
|
+
rescue StandardError => e
|
36
|
+
raise if Chef::Config[:verbosity] == 2
|
37
|
+
ui.warn "bootstrap of node #{node_name} exited with error"
|
38
|
+
humanize_exception(e)
|
39
|
+
false
|
40
|
+
end
|
41
|
+
|
42
|
+
# rubocop:disable Metrics/AbcSize
|
43
|
+
def setup_bootstrap_args(args, data)
|
44
|
+
# We need to remove the --bootstrap option, if it exists
|
45
|
+
args -= ['--bootstrap']
|
46
|
+
args[1] = data['ssh_host']
|
47
|
+
|
48
|
+
# And set up the node-specific data but ONLY if defined
|
49
|
+
args += ['-N', data['name']] if data['name']
|
50
|
+
args += ['-E', data['chef_environment']] if data['chef_environment']
|
51
|
+
args += ['--ssh-port', data['ssh_port']] if data['ssh_port']
|
52
|
+
args += ['--run-list', data['run_list'].join(',')] if data['run_list']
|
53
|
+
attrs = attributes_for_bootstrap(data)
|
54
|
+
args += ['--json-attributes', attrs.to_json] unless attrs.empty?
|
55
|
+
args
|
56
|
+
end
|
57
|
+
# rubocop:enable Metrics/AbcSize
|
58
|
+
|
59
|
+
# for bootstrap, attributes have to include tags
|
60
|
+
def attributes_for_bootstrap(data)
|
61
|
+
attrs = data['normal'] || {}
|
62
|
+
attrs['tags'] = data['tags'] if data['tags']
|
63
|
+
attrs
|
64
|
+
end
|
65
|
+
|
66
|
+
def delete_client_node(node_name)
|
67
|
+
ui.info("Node #{node_name} exists and will be overwritten")
|
68
|
+
# delete node first so vault refresh does not pick up existing node
|
69
|
+
rest.delete("nodes/#{node_name}")
|
70
|
+
rest.delete("clients/#{node_name}")
|
71
|
+
rescue Net::HTTPServerException => e
|
72
|
+
raise unless e.response.code == '404'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Christine Draper (<christine_draper@thirdwaveinsights.com>)
|
3
|
+
# Copyright:: Copyright (c) 2014 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
|
+
require 'chef/environment'
|
20
|
+
|
21
|
+
module KnifeTopo
|
22
|
+
# Knife topo helpers
|
23
|
+
module CommandHelper
|
24
|
+
# initialize args for another knife command
|
25
|
+
def initialize_cmd_args(args, name_args, new_name_args)
|
26
|
+
args = args.dup
|
27
|
+
args.shift(2 + name_args.length)
|
28
|
+
new_name_args + args
|
29
|
+
end
|
30
|
+
|
31
|
+
# run another knife command
|
32
|
+
def run_cmd(command_class, args)
|
33
|
+
command = command_class.new(args)
|
34
|
+
command.config[:config_file] = config[:config_file]
|
35
|
+
command.configure_chef
|
36
|
+
command_class.load_deps
|
37
|
+
command.run
|
38
|
+
|
39
|
+
command
|
40
|
+
end
|
41
|
+
|
42
|
+
# check if resource exists
|
43
|
+
def resource_exists?(relative_path)
|
44
|
+
rest.get_rest(relative_path)
|
45
|
+
true
|
46
|
+
rescue Net::HTTPServerException => e
|
47
|
+
raise unless e.response.code == '404'
|
48
|
+
false
|
49
|
+
end
|
50
|
+
|
51
|
+
# make sure the chef environment exists
|
52
|
+
def check_chef_env(chef_env_name)
|
53
|
+
return unless chef_env_name
|
54
|
+
Chef::Environment.load(chef_env_name) if chef_env_name
|
55
|
+
rescue Net::HTTPServerException => e
|
56
|
+
raise unless e.to_s =~ /^404/
|
57
|
+
ui.info 'Creating chef environment ' + chef_env_name
|
58
|
+
chef_env = Chef::Environment.new
|
59
|
+
chef_env.name(chef_env_name)
|
60
|
+
chef_env.create
|
61
|
+
chef_env
|
62
|
+
end
|
63
|
+
|
64
|
+
def most_common(vals)
|
65
|
+
return if vals.length == 0
|
66
|
+
vals.group_by do |val|
|
67
|
+
val
|
68
|
+
end.values.max_by(&:size).first
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Christine Draper (<christine_draper@thirdwaveinsights.com>)
|
3
|
+
# Copyright:: Copyright (c) 2014 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
|
+
require 'chef/topology'
|
20
|
+
require 'chef/knife/core/object_loader'
|
21
|
+
require 'chef/data_bag'
|
22
|
+
require 'chef/data_bag_item'
|
23
|
+
require 'chef/knife'
|
24
|
+
|
25
|
+
module KnifeTopo
|
26
|
+
# Topology loaders
|
27
|
+
module Loader
|
28
|
+
attr_reader :ui, :loader
|
29
|
+
|
30
|
+
# Loader to get data bag items from file
|
31
|
+
def loader
|
32
|
+
@loader ||= Chef::Knife::Core::ObjectLoader.new(Chef::DataBagItem, ui)
|
33
|
+
end
|
34
|
+
|
35
|
+
def load_local_topo_or_exit(topo_name)
|
36
|
+
filepath = get_local_topo_path(topo_name)
|
37
|
+
msg = "Topology file #{filepath} not found - use " \
|
38
|
+
"'knife topo import' first"
|
39
|
+
check_file(filepath, msg)
|
40
|
+
load_topo_from_file_or_exit(filepath)
|
41
|
+
end
|
42
|
+
|
43
|
+
def load_topo_from_file_or_exit(filepath, format = nil)
|
44
|
+
check_file(filepath)
|
45
|
+
data = loader.object_from_file(filepath)
|
46
|
+
format ||= auto_detect_format(data)
|
47
|
+
topo = Chef::Topology.convert_from(format, data)
|
48
|
+
topo.data_bag(topo_bag_name)
|
49
|
+
topo
|
50
|
+
end
|
51
|
+
|
52
|
+
def check_file(filepath, msg = nil)
|
53
|
+
return if loader.file_exists_and_is_readable?(filepath)
|
54
|
+
msg ||= "Topology file #{filepath} not found"
|
55
|
+
ui.fatal(msg)
|
56
|
+
exit(1)
|
57
|
+
end
|
58
|
+
|
59
|
+
def auto_detect_format(data)
|
60
|
+
return 'topo_v1' if data['cookbook_attributes']
|
61
|
+
'default'
|
62
|
+
end
|
63
|
+
|
64
|
+
def get_local_topo_path(topo_name)
|
65
|
+
File.join(
|
66
|
+
Dir.pwd,
|
67
|
+
topologies_path,
|
68
|
+
topo_bag_name,
|
69
|
+
topo_name + '.json'
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
def load_topo_from_server(topo_name)
|
74
|
+
Chef::Topology.load(topo_bag_name, topo_name)
|
75
|
+
rescue Net::HTTPServerException => e
|
76
|
+
raise unless e.to_s =~ /^404/
|
77
|
+
end
|
78
|
+
|
79
|
+
def load_topo_from_server_or_exit(topo_name)
|
80
|
+
topo = load_topo_from_server(topo_name)
|
81
|
+
unless topo
|
82
|
+
ui.fatal("Topology #{topo_bag_name}/#{@topo_name} does not exist " \
|
83
|
+
"on the server - use 'knife topo create' first")
|
84
|
+
exit(1)
|
85
|
+
end
|
86
|
+
topo
|
87
|
+
end
|
88
|
+
|
89
|
+
# Name of the topology bag
|
90
|
+
def topo_bag_name
|
91
|
+
@topo_bag_name ||= config[:data_bag]
|
92
|
+
@topo_bag_name ||= 'topologies'
|
93
|
+
end
|
94
|
+
|
95
|
+
# Path for the topologies data bags.
|
96
|
+
# For now, use the standard data_bags path for our topologies bags
|
97
|
+
def topologies_path
|
98
|
+
@topologies_path ||= 'data_bags'
|
99
|
+
end
|
100
|
+
|
101
|
+
def create_topo_bag
|
102
|
+
data_bag = Chef::DataBag.new
|
103
|
+
data_bag.name(topo_bag_name)
|
104
|
+
data_bag.create
|
105
|
+
rescue Net::HTTPServerException => e
|
106
|
+
raise unless e.to_s =~ /^409/
|
107
|
+
end
|
108
|
+
|
109
|
+
def list_topo_bag
|
110
|
+
Chef::DataBag.load(topo_bag_name)
|
111
|
+
rescue Net::HTTPServerException => e
|
112
|
+
raise unless e.to_s =~ /^404/
|
113
|
+
end
|
114
|
+
|
115
|
+
def load_node_data(node_name, min_priority = 'default')
|
116
|
+
node_data = {}
|
117
|
+
node = Chef::Node.load(node_name)
|
118
|
+
%w(name tags chef_environment run_list).each do |key|
|
119
|
+
node_data[key] = node.send(key)
|
120
|
+
end
|
121
|
+
node_data = node_data.merge(priority_attrs(node, min_priority))
|
122
|
+
end
|
123
|
+
|
124
|
+
def priority_attrs(node, min_priority = 'default')
|
125
|
+
attrs = {}
|
126
|
+
p = KnifeTopo::PRIORITIES
|
127
|
+
min_index = p.index(min_priority)
|
128
|
+
p.each_index do |index|
|
129
|
+
next if index < min_index
|
130
|
+
key = p[index]
|
131
|
+
attrs[key] = node.send(key)
|
132
|
+
attrs.delete(key) if attrs[key].empty?
|
133
|
+
end
|
134
|
+
attrs
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Christine Draper (<christine_draper@thirdwaveinsights.com>)
|
3
|
+
# Copyright:: Copyright (c) 2014 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
|
+
require 'chef/node'
|
20
|
+
require 'chef/run_list'
|
21
|
+
require 'chef/mixin/deep_merge'
|
22
|
+
require 'chef/rest'
|
23
|
+
|
24
|
+
module KnifeTopo
|
25
|
+
# Node update helper for knife topo
|
26
|
+
module NodeUpdateHelper
|
27
|
+
# Update an existing node
|
28
|
+
def update_node(node_updates)
|
29
|
+
config[:disable_editing] = true
|
30
|
+
|
31
|
+
begin
|
32
|
+
# load then update and save the node
|
33
|
+
node = Chef::Node.load(node_updates['name'])
|
34
|
+
|
35
|
+
env = node_updates['chef_environment']
|
36
|
+
check_chef_env(env) unless env == node['chef_environment']
|
37
|
+
do_node_updates(node, node_updates)
|
38
|
+
|
39
|
+
rescue Net::HTTPServerException => e
|
40
|
+
raise unless e.to_s =~ /^404/
|
41
|
+
# Node has not been created
|
42
|
+
end
|
43
|
+
|
44
|
+
node
|
45
|
+
end
|
46
|
+
|
47
|
+
def do_node_updates(node, node_updates)
|
48
|
+
updated = update_node_with_values(node, node_updates)
|
49
|
+
if updated
|
50
|
+
ui.info "Updating #{updated.join(', ')} on node #{node.name}"
|
51
|
+
node.save
|
52
|
+
ui.output(format_for_display(node)) if config[:print_after]
|
53
|
+
else
|
54
|
+
ui.info "No updates found for node #{node.name}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Update original node, return list of updated properties.
|
59
|
+
def update_node_with_values(node, updates)
|
60
|
+
updated = []
|
61
|
+
|
62
|
+
# merge the normal attributes (but not tags)
|
63
|
+
updated << 'normal' if update_attrs(node, updates['normal'])
|
64
|
+
|
65
|
+
# update runlist
|
66
|
+
updated << 'run_list' if update_runlist(node, updates['run_list'])
|
67
|
+
|
68
|
+
# update chef env
|
69
|
+
if update_chef_env(node, updates['chef_environment'])
|
70
|
+
updated << 'chef_environment'
|
71
|
+
end
|
72
|
+
|
73
|
+
# merge tags
|
74
|
+
updated << 'tags' if update_tags(node, updates['tags'])
|
75
|
+
|
76
|
+
# return false if no updates, else return array of property names
|
77
|
+
updated.length > 0 && updated
|
78
|
+
end
|
79
|
+
|
80
|
+
# Update methods all return true if an actual update is made
|
81
|
+
def update_attrs(node, attrs)
|
82
|
+
return false unless attrs
|
83
|
+
attrs.delete('tags')
|
84
|
+
original = node.normal.clone
|
85
|
+
node.normal = Chef::Mixin::DeepMerge.merge(node.normal, attrs)
|
86
|
+
original != node.normal
|
87
|
+
end
|
88
|
+
|
89
|
+
def update_runlist(node, runlist)
|
90
|
+
# node.run_list MUST be lhs of != to use override operator
|
91
|
+
return false unless runlist && node.run_list != runlist
|
92
|
+
updated_run_list = Chef::RunList.new
|
93
|
+
runlist.each { |e| updated_run_list << e }
|
94
|
+
node.run_list(*updated_run_list)
|
95
|
+
true
|
96
|
+
end
|
97
|
+
|
98
|
+
def update_chef_env(node, env)
|
99
|
+
return false unless env && env != node.chef_environment
|
100
|
+
node.chef_environment(env)
|
101
|
+
true
|
102
|
+
end
|
103
|
+
|
104
|
+
def update_tags(node, tags)
|
105
|
+
return false unless tags
|
106
|
+
orig_num_tags = node.tags.length
|
107
|
+
node.tag(*tags)
|
108
|
+
node.tags.length > orig_num_tags
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,76 @@
|
|
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
|
+
# The processor class converts a topology into node data and artifacts,
|
21
|
+
# based on the strategy
|
22
|
+
#
|
23
|
+
|
24
|
+
require 'chef/mixin/deep_merge'
|
25
|
+
module KnifeTopo
|
26
|
+
# Base processor
|
27
|
+
class Processor
|
28
|
+
# rubocop:disable Style/ClassVars
|
29
|
+
@@processor_classes = {}
|
30
|
+
|
31
|
+
def self.register_processor(strategy, class_name)
|
32
|
+
@@processor_classes[strategy] = class_name
|
33
|
+
end
|
34
|
+
|
35
|
+
# Get the right processor
|
36
|
+
def self.processor(topo)
|
37
|
+
strategy = topo.strategy
|
38
|
+
processor_class = @@processor_classes[strategy]
|
39
|
+
processor_class = load_processor(strategy) unless processor_class
|
40
|
+
|
41
|
+
Object.const_get(processor_class).new(topo)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.load_processor(strategy)
|
45
|
+
require "chef/knife/topo/processor/#{strategy}"
|
46
|
+
@@processor_classes[strategy]
|
47
|
+
rescue LoadError
|
48
|
+
STDERR.puts("#{strategy} is not a known strategy")
|
49
|
+
exit(1)
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.for_topo(topo)
|
53
|
+
processor(topo)
|
54
|
+
end
|
55
|
+
|
56
|
+
attr_accessor :input
|
57
|
+
|
58
|
+
def initialize(topo)
|
59
|
+
@topo = topo
|
60
|
+
end
|
61
|
+
|
62
|
+
# Other processors should override the following methods
|
63
|
+
register_processor('direct_to_node', name)
|
64
|
+
|
65
|
+
def generate_nodes
|
66
|
+
@topo.merged_nodes
|
67
|
+
end
|
68
|
+
|
69
|
+
def generate_artifacts(_context = {})
|
70
|
+
{}
|
71
|
+
end
|
72
|
+
|
73
|
+
def upload_artifacts(_context = {})
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|