spiceweasel 1.2.0 → 2.0.0
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.
- data/LICENSE +201 -0
- data/README.md +141 -241
- data/bin/spiceweasel +2 -104
- data/lib/spiceweasel.rb +5 -11
- data/lib/spiceweasel/cli.rb +249 -83
- data/lib/spiceweasel/clusters.rb +53 -0
- data/lib/spiceweasel/config.rb +46 -0
- data/lib/spiceweasel/cookbooks.rb +105 -0
- data/lib/spiceweasel/data_bags.rb +89 -0
- data/lib/spiceweasel/environments.rb +103 -0
- data/lib/spiceweasel/execute.rb +42 -0
- data/lib/spiceweasel/extract_local.rb +130 -0
- data/lib/spiceweasel/log.rb +30 -0
- data/lib/spiceweasel/nodes.rb +126 -0
- data/lib/spiceweasel/roles.rb +131 -0
- data/lib/spiceweasel/version.rb +1 -1
- data/spec/bin/spiceweasel_spec.rb +44 -12
- metadata +106 -25
- data/lib/spiceweasel/cookbook_data.rb +0 -66
- data/lib/spiceweasel/cookbook_list.rb +0 -96
- data/lib/spiceweasel/data_bag_list.rb +0 -81
- data/lib/spiceweasel/directory_extractor.rb +0 -121
- data/lib/spiceweasel/environment_list.rb +0 -96
- data/lib/spiceweasel/node_list.rb +0 -98
- data/lib/spiceweasel/role_list.rb +0 -124
data/bin/spiceweasel
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
#
|
4
4
|
# Author:: Matt Ray (<matt@opscode.com>)
|
5
5
|
#
|
6
|
-
# Copyright:: 2011, Opscode, Inc <legal@opscode.com>
|
6
|
+
# Copyright:: 2011-2012, Opscode, Inc <legal@opscode.com>
|
7
7
|
#
|
8
8
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
9
9
|
# you may not use this file except in compliance with the License.
|
@@ -18,108 +18,6 @@
|
|
18
18
|
# limitations under the License.
|
19
19
|
#
|
20
20
|
|
21
|
-
require 'json'
|
22
|
-
require 'yaml'
|
23
|
-
|
24
21
|
require 'spiceweasel'
|
25
22
|
|
26
|
-
|
27
|
-
|
28
|
-
#process command line options
|
29
|
-
begin
|
30
|
-
ARGV << "-h" if ARGV.empty?
|
31
|
-
cli = Spiceweasel::CLI.new
|
32
|
-
cli.parse_options
|
33
|
-
Spiceweasel::DEBUG = cli.config[:debug]
|
34
|
-
Spiceweasel::PARALLEL = cli.config[:parallel]
|
35
|
-
Spiceweasel::SITEINSTALL = cli.config[:siteinstall]
|
36
|
-
Spiceweasel::NOVALIDATION = cli.config[:novalidation]
|
37
|
-
Spiceweasel::EXTRACTLOCAL = cli.config[:extractlocal]
|
38
|
-
Spiceweasel::EXTRACTYAML = cli.config[:extractyaml]
|
39
|
-
Spiceweasel::EXTRACTJSON = cli.config[:extractjson]
|
40
|
-
rescue OptionParser::InvalidOption => e
|
41
|
-
STDERR.puts e.message
|
42
|
-
puts cli.opt_parser.to_s
|
43
|
-
exit(-1)
|
44
|
-
end
|
45
|
-
|
46
|
-
if cli.config[:knifeconfig]
|
47
|
-
options['knife_options'] += " -c " + cli.config[:knifeconfig]
|
48
|
-
end
|
49
|
-
|
50
|
-
if cli.config[:serverurl]
|
51
|
-
options['knife_options'] += " --server-url " + cli.config[:serverurl]
|
52
|
-
end
|
53
|
-
|
54
|
-
if Spiceweasel::EXTRACTLOCAL || Spiceweasel::EXTRACTJSON || Spiceweasel::EXTRACTYAML
|
55
|
-
input = Spiceweasel::DirectoryExtractor.parse_objects
|
56
|
-
STDOUT.puts "DEBUG: extract input: #{input}" if Spiceweasel::DEBUG
|
57
|
-
else
|
58
|
-
begin
|
59
|
-
file = ARGV.last
|
60
|
-
STDOUT.puts "DEBUG: file: #{file}" if Spiceweasel::DEBUG
|
61
|
-
if (file.end_with?(".yml"))
|
62
|
-
input = YAML.load_file ARGV.last
|
63
|
-
elsif (file.end_with?(".json"))
|
64
|
-
input = JSON.parse(File.read(ARGV.last))
|
65
|
-
else
|
66
|
-
STDERR.puts "ERROR: Unknown file type, please use a file ending with either '.json' or '.yml'."
|
67
|
-
exit(-1)
|
68
|
-
end
|
69
|
-
rescue Psych::SyntaxError => e
|
70
|
-
STDERR.puts e.message
|
71
|
-
STDERR.puts "ERROR: Parsing error in #{file}."
|
72
|
-
exit(-1)
|
73
|
-
rescue JSON::ParserError => e
|
74
|
-
STDERR.puts e.message
|
75
|
-
STDERR.puts "ERROR: Parsing error in #{file}."
|
76
|
-
exit(-1)
|
77
|
-
rescue Exception
|
78
|
-
STDERR.puts "ERROR: No infrastructure .json or .yml file provided."
|
79
|
-
puts cli.opt_parser.to_s
|
80
|
-
exit(-1)
|
81
|
-
end
|
82
|
-
STDOUT.puts "DEBUG: file input: #{input}" if Spiceweasel::DEBUG
|
83
|
-
end
|
84
|
-
|
85
|
-
create = String.new()
|
86
|
-
delete = String.new()
|
87
|
-
|
88
|
-
cookbook_list = Spiceweasel::CookbookList.new(input['cookbooks'], options)
|
89
|
-
environment_list = Spiceweasel::EnvironmentList.new(input['environments'], cookbook_list, options)
|
90
|
-
role_list = Spiceweasel::RoleList.new(input['roles'], environment_list, cookbook_list, options)
|
91
|
-
data_bag_list = Spiceweasel::DataBagList.new(input['data bags'], options)
|
92
|
-
node_list = Spiceweasel::NodeList.new(input['nodes'], cookbook_list, environment_list, role_list, options)
|
93
|
-
|
94
|
-
create += cookbook_list.create
|
95
|
-
create += environment_list.create
|
96
|
-
create += role_list.create
|
97
|
-
create += data_bag_list.create
|
98
|
-
create += node_list.create
|
99
|
-
|
100
|
-
delete += cookbook_list.delete
|
101
|
-
delete += environment_list.delete
|
102
|
-
delete += role_list.delete
|
103
|
-
delete += data_bag_list.delete
|
104
|
-
delete += node_list.delete
|
105
|
-
|
106
|
-
#just print the knife commands, do not execute
|
107
|
-
#if cli.config[:dryrun]
|
108
|
-
if cli.config[:delete]
|
109
|
-
puts delete unless delete.empty?
|
110
|
-
elsif cli.config[:rebuild]
|
111
|
-
puts delete unless delete.empty?
|
112
|
-
puts create unless create.empty?
|
113
|
-
else
|
114
|
-
if Spiceweasel::EXTRACTJSON
|
115
|
-
puts JSON.pretty_generate(input)
|
116
|
-
elsif Spiceweasel::EXTRACTYAML
|
117
|
-
puts input.to_yaml
|
118
|
-
else
|
119
|
-
puts create unless create.empty?
|
120
|
-
end
|
121
|
-
end
|
122
|
-
#else
|
123
|
-
#eventually we will execute instead of printing knife commands
|
124
|
-
#puts "BAM!"
|
125
|
-
#end
|
23
|
+
Spiceweasel::CLI.new.run
|
data/lib/spiceweasel.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#
|
2
2
|
# Author:: Matt Ray (<matt@opscode.com>)
|
3
3
|
#
|
4
|
-
# Copyright:: 2011, Opscode, Inc <legal@opscode.com>
|
4
|
+
# Copyright:: 2011-2012, Opscode, Inc <legal@opscode.com>
|
5
5
|
#
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
7
|
# you may not use this file except in compliance with the License.
|
@@ -16,13 +16,7 @@
|
|
16
16
|
# limitations under the License.
|
17
17
|
#
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
autoload :RoleList, 'spiceweasel/role_list'
|
24
|
-
autoload :DataBagList, 'spiceweasel/data_bag_list'
|
25
|
-
autoload :NodeList, 'spiceweasel/node_list'
|
26
|
-
autoload :DirectoryExtractor, 'spiceweasel/directory_extractor'
|
27
|
-
autoload :CookbookData, 'spiceweasel/cookbook_data'
|
28
|
-
end
|
19
|
+
require 'spiceweasel/version'
|
20
|
+
require 'spiceweasel/cli'
|
21
|
+
require 'spiceweasel/config'
|
22
|
+
require 'spiceweasel/log'
|
data/lib/spiceweasel/cli.rb
CHANGED
@@ -1,85 +1,251 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
:long => "--extractyaml",
|
19
|
-
:description => "Use contents of local chef repository directories to generate YAML spiceweasel manifest"
|
20
|
-
|
21
|
-
option :debug,
|
22
|
-
:long => "--debug",
|
23
|
-
:description => "Verbose debugging messages",
|
24
|
-
:boolean => true
|
25
|
-
|
26
|
-
option :delete,
|
27
|
-
:short => "-d",
|
28
|
-
:long => "--delete",
|
29
|
-
:description => "Print the knife commands to delete the infrastructure",
|
30
|
-
:boolean => true
|
31
|
-
|
32
|
-
option :dryrun,
|
33
|
-
:long => "--dryrun",
|
34
|
-
:description => "Print the knife commands to be executed to STDOUT",
|
35
|
-
:boolean => true
|
36
|
-
|
37
|
-
option :help,
|
38
|
-
:short => "-h",
|
39
|
-
:long => "--help",
|
40
|
-
:description => "Show this message",
|
41
|
-
:on => :tail,
|
42
|
-
:boolean => true,
|
43
|
-
:show_options => true,
|
44
|
-
:exit => 0
|
45
|
-
|
46
|
-
option :serverurl,
|
47
|
-
:short => "-s URL",
|
48
|
-
:long => "--server-url URL",
|
49
|
-
:description => "Specify the Chef Server URL"
|
50
|
-
|
51
|
-
option :knifeconfig,
|
52
|
-
:short => "-c CONFIG",
|
53
|
-
:long => "--knifeconfig CONFIG",
|
54
|
-
:description => "Specify the knife.rb configuration file"
|
55
|
-
|
56
|
-
option :novalidation,
|
57
|
-
:long => "--novalidation",
|
58
|
-
:description => "Disable validation",
|
59
|
-
:boolean => true
|
60
|
-
|
61
|
-
option :parallel,
|
62
|
-
:long => "--parallel",
|
63
|
-
:description => "Use the GNU 'parallel' command to parallelize 'knife VENDOR server create' commands that are not order-dependent",
|
64
|
-
:boolean => true
|
65
|
-
|
66
|
-
option :rebuild,
|
67
|
-
:short => "-r",
|
68
|
-
:long => "--rebuild",
|
69
|
-
:description => "Print the knife commands to be delete and recreate the infrastructure",
|
70
|
-
:boolean => true
|
71
|
-
|
72
|
-
option :siteinstall,
|
73
|
-
:long => "--siteinstall",
|
74
|
-
:description => "Use the 'install' command with 'knife cookbook site' instead of the default 'download'",
|
75
|
-
:boolean => true
|
76
|
-
|
77
|
-
option :version,
|
78
|
-
:short => "-v",
|
79
|
-
:long => "--version",
|
80
|
-
:description => "Show spiceweasel version",
|
81
|
-
:boolean => true,
|
82
|
-
:proc => lambda {|v| puts "Spiceweasel: #{Spiceweasel::VERSION}" },
|
83
|
-
:exit => 0
|
1
|
+
#
|
2
|
+
# Author:: Matt Ray (<matt@opscode.com>)
|
3
|
+
#
|
4
|
+
# Copyright:: 2011-2012, Opscode, Inc <legal@opscode.com>
|
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
|
+
#
|
84
18
|
|
19
|
+
require 'mixlib/cli'
|
20
|
+
require 'json'
|
21
|
+
require 'yaml'
|
22
|
+
|
23
|
+
require 'spiceweasel'
|
24
|
+
require 'spiceweasel/cookbooks'
|
25
|
+
require 'spiceweasel/environments'
|
26
|
+
require 'spiceweasel/roles'
|
27
|
+
require 'spiceweasel/data_bags'
|
28
|
+
require 'spiceweasel/nodes'
|
29
|
+
require 'spiceweasel/clusters'
|
30
|
+
require 'spiceweasel/extract_local'
|
31
|
+
require 'spiceweasel/execute'
|
32
|
+
|
33
|
+
module Spiceweasel
|
34
|
+
class CLI
|
35
|
+
include Mixlib::CLI
|
36
|
+
|
37
|
+
banner('Usage: spiceweasel [option] file
|
38
|
+
spiceweasel [option] --extractlocal')
|
39
|
+
|
40
|
+
option :clusterfile,
|
41
|
+
:long => '--cluster-file file',
|
42
|
+
:description => 'Specify an additional cluster manifest file, overriding any other node or cluster definitions'
|
43
|
+
|
44
|
+
option :debug,
|
45
|
+
:long => '--debug',
|
46
|
+
:description => 'Verbose debugging messages',
|
47
|
+
:boolean => true
|
48
|
+
|
49
|
+
option :delete,
|
50
|
+
:short => '-d',
|
51
|
+
:long => '--delete',
|
52
|
+
:description => 'Print the knife commands to delete the infrastructure',
|
53
|
+
:boolean => true
|
54
|
+
|
55
|
+
option :execute,
|
56
|
+
:short => '-e',
|
57
|
+
:long => '--execute',
|
58
|
+
:description => 'Execute the knife commands to create the infrastructure directly',
|
59
|
+
:boolean => true
|
60
|
+
|
61
|
+
option :extractlocal,
|
62
|
+
:long => '--extractlocal',
|
63
|
+
:description => 'Use contents of local chef repository directories to generate knife commands to build infrastructure'
|
64
|
+
|
65
|
+
option :extractjson,
|
66
|
+
:long => '--extractjson',
|
67
|
+
:description => 'Use contents of local chef repository directories to generate JSON spiceweasel manifest'
|
68
|
+
|
69
|
+
option :extractyaml,
|
70
|
+
:long => '--extractyaml',
|
71
|
+
:description => 'Use contents of local chef repository directories to generate YAML spiceweasel manifest'
|
72
|
+
|
73
|
+
option :help,
|
74
|
+
:short => '-h',
|
75
|
+
:long => '--help',
|
76
|
+
:description => 'Show this message',
|
77
|
+
:on => :tail,
|
78
|
+
:boolean => true,
|
79
|
+
:show_options => true,
|
80
|
+
:exit => 0
|
81
|
+
|
82
|
+
option :knifeconfig,
|
83
|
+
:short => '-c CONFIG',
|
84
|
+
:long => '--knifeconfig CONFIG',
|
85
|
+
:description => 'Specify the knife.rb configuration file'
|
86
|
+
|
87
|
+
option :log_level,
|
88
|
+
:short => "-l LEVEL",
|
89
|
+
:long => "--log_level LEVEL",
|
90
|
+
:description => "Set the log level (debug, info, warn, error, fatal)",
|
91
|
+
:proc => lambda { |l| l.to_sym }
|
92
|
+
|
93
|
+
option :log_location,
|
94
|
+
:short => "-L LOGLOCATION",
|
95
|
+
:long => "--logfile LOGLOCATION",
|
96
|
+
:description => "Set the log file location, defaults to STDOUT",
|
97
|
+
:proc => nil
|
98
|
+
|
99
|
+
option :novalidation,
|
100
|
+
:long => '--novalidation',
|
101
|
+
:description => 'Disable validation',
|
102
|
+
:boolean => true
|
103
|
+
|
104
|
+
option :parallel,
|
105
|
+
:long => '--parallel',
|
106
|
+
:description => "Use the GNU 'parallel' command to parallelize 'knife VENDOR server create' commands where applicable",
|
107
|
+
:boolean => true
|
108
|
+
|
109
|
+
option :rebuild,
|
110
|
+
:short => '-r',
|
111
|
+
:long => '--rebuild',
|
112
|
+
:description => 'Print the knife commands to delete and recreate the infrastructure',
|
113
|
+
:boolean => true
|
114
|
+
|
115
|
+
option :serverurl,
|
116
|
+
:short => '-s URL',
|
117
|
+
:long => '--server-url URL',
|
118
|
+
:description => 'Specify the Chef Server URL'
|
119
|
+
|
120
|
+
option :siteinstall,
|
121
|
+
:long => '--siteinstall',
|
122
|
+
:description => "Use the 'install' command with 'knife cookbook site' instead of the default 'download'",
|
123
|
+
:boolean => true
|
124
|
+
|
125
|
+
option :version,
|
126
|
+
:short => '-v',
|
127
|
+
:long => '--version',
|
128
|
+
:description => 'Show spiceweasel version',
|
129
|
+
:boolean => true,
|
130
|
+
:proc => lambda { |v| puts "Spiceweasel: #{::Spiceweasel::VERSION}" },
|
131
|
+
:exit => 0
|
132
|
+
|
133
|
+
def run
|
134
|
+
if Spiceweasel::Config[:extractlocal] || Spiceweasel::Config[:extractjson] || Spiceweasel::Config[:extractyaml]
|
135
|
+
manifest = Spiceweasel::ExtractLocal.parse_objects
|
136
|
+
else
|
137
|
+
manifest = parse_and_validate_input(ARGV.last)
|
138
|
+
if Spiceweasel::Config[:clusterfile]
|
139
|
+
# if we have a cluster file, override any nodes or clusters in the original manifest
|
140
|
+
manifest['nodes'] = manifest['clusters'] = {}
|
141
|
+
manifest.merge!(parse_and_validate_input(Spiceweasel::Config[:clusterfile]))
|
142
|
+
end
|
143
|
+
end
|
144
|
+
Spiceweasel::Log.debug("file manifest: #{manifest}")
|
145
|
+
|
146
|
+
cookbooks = Cookbooks.new(manifest['cookbooks'])
|
147
|
+
environments = Environments.new(manifest['environments'], cookbooks)
|
148
|
+
roles = Roles.new(manifest['roles'], environments, cookbooks)
|
149
|
+
data_bags = DataBags.new(manifest['data bags'])
|
150
|
+
nodes = Nodes.new(manifest['nodes'], cookbooks, environments, roles)
|
151
|
+
clusters = Clusters.new(manifest['clusters'], cookbooks, environments, roles)
|
152
|
+
|
153
|
+
create = cookbooks.create + environments.create + roles.create + data_bags.create + nodes.create + clusters.create
|
154
|
+
delete = cookbooks.delete + environments.delete + roles.delete + data_bags.delete + nodes.delete + clusters.delete
|
155
|
+
|
156
|
+
#trim trailing whitespace
|
157
|
+
create.each {|x| x.rstrip!}
|
158
|
+
delete.each {|x| x.rstrip!}
|
159
|
+
|
160
|
+
if Spiceweasel::Config[:extractjson]
|
161
|
+
puts JSON.pretty_generate(manifest)
|
162
|
+
elsif Spiceweasel::Config[:extractyaml]
|
163
|
+
puts manifest.to_yaml
|
164
|
+
elsif Spiceweasel::Config[:delete]
|
165
|
+
if Spiceweasel::Config[:execute]
|
166
|
+
Execute.new(delete)
|
167
|
+
else
|
168
|
+
puts delete unless delete.empty?
|
169
|
+
end
|
170
|
+
elsif Spiceweasel::Config[:rebuild]
|
171
|
+
if Spiceweasel::Config[:execute]
|
172
|
+
Execute.new(delete)
|
173
|
+
Execute.new(create)
|
174
|
+
else
|
175
|
+
puts delete unless delete.empty?
|
176
|
+
puts create unless create.empty?
|
177
|
+
end
|
178
|
+
else
|
179
|
+
if Spiceweasel::Config[:execute]
|
180
|
+
Execute.new(create)
|
181
|
+
else
|
182
|
+
puts create unless create.empty?
|
183
|
+
end
|
184
|
+
end
|
185
|
+
exit 0
|
186
|
+
end
|
187
|
+
|
188
|
+
def initialize(argv=[])
|
189
|
+
super()
|
190
|
+
parse_and_validate_options
|
191
|
+
Config.merge!(@config)
|
192
|
+
configure_logging
|
193
|
+
Spiceweasel::Log.debug("Validation of the manifest has been turned off.") if Spiceweasel::Config[:novalidation]
|
194
|
+
end
|
195
|
+
|
196
|
+
def parse_and_validate_options
|
197
|
+
ARGV << "-h" if ARGV.empty?
|
198
|
+
begin
|
199
|
+
parse_options
|
200
|
+
if Spiceweasel::Config[:knifeconfig]
|
201
|
+
Spiceweasel::Config[:knife_options] = "-c #{Spiceweasel::Config[:knifeconfig]} "
|
202
|
+
end
|
203
|
+
if Spiceweasel::Config[:serverurl]
|
204
|
+
Spiceweasel::Config[:knife_options] += "--server-url #{Spiceweasel::Config[:serverurl]} "
|
205
|
+
end
|
206
|
+
rescue OptionParser::InvalidOption => e
|
207
|
+
STDERR.puts e.message
|
208
|
+
puts opt_parser.to_s
|
209
|
+
exit(-1)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def configure_logging
|
214
|
+
Spiceweasel::Log.init(Spiceweasel::Config[:log_location])
|
215
|
+
Spiceweasel::Log.level = Spiceweasel::Config[:log_level]
|
216
|
+
Spiceweasel::Log.level = :debug if Spiceweasel::Config[:debug]
|
217
|
+
end
|
218
|
+
|
219
|
+
def parse_and_validate_input(file)
|
220
|
+
begin
|
221
|
+
Spiceweasel::Log.debug("file: #{file}")
|
222
|
+
if !File.file?(file)
|
223
|
+
STDERR.puts "ERROR: #{file} is an invalid manifest file, please check your path."
|
224
|
+
exit(-1)
|
225
|
+
end
|
226
|
+
if (file.end_with?(".yml"))
|
227
|
+
output = YAML.load_file(file)
|
228
|
+
elsif (file.end_with?(".json"))
|
229
|
+
output = JSON.parse(File.read(file))
|
230
|
+
else
|
231
|
+
STDERR.puts "ERROR: #{file} is an unknown file type, please use a file ending with either '.json' or '.yml'."
|
232
|
+
exit(-1)
|
233
|
+
end
|
234
|
+
rescue Psych::SyntaxError => e
|
235
|
+
STDERR.puts e.message
|
236
|
+
STDERR.puts "ERROR: Parsing error in #{file}."
|
237
|
+
exit(-1)
|
238
|
+
rescue JSON::ParserError => e
|
239
|
+
STDERR.puts e.message
|
240
|
+
STDERR.puts "ERROR: Parsing error in #{file}."
|
241
|
+
exit(-1)
|
242
|
+
rescue Exception
|
243
|
+
STDERR.puts "ERROR: No manifest .json or .yml file provided."
|
244
|
+
puts opt_parser.to_s
|
245
|
+
exit(-1)
|
246
|
+
end
|
247
|
+
output
|
248
|
+
end
|
249
|
+
|
250
|
+
end
|
85
251
|
end
|