puppetserver-ca 0.3.1 → 0.4.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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/lib/puppetserver/ca/action/clean.rb +102 -0
  3. data/lib/puppetserver/ca/action/create.rb +161 -0
  4. data/lib/puppetserver/ca/action/generate.rb +313 -0
  5. data/lib/puppetserver/ca/action/import.rb +132 -0
  6. data/lib/puppetserver/ca/action/list.rb +132 -0
  7. data/lib/puppetserver/ca/action/revoke.rb +101 -0
  8. data/lib/puppetserver/ca/action/sign.rb +126 -0
  9. data/lib/puppetserver/ca/certificate_authority.rb +224 -0
  10. data/lib/puppetserver/ca/cli.rb +17 -16
  11. data/lib/puppetserver/ca/config/puppet.rb +242 -0
  12. data/lib/puppetserver/ca/config/puppetserver.rb +85 -0
  13. data/lib/puppetserver/ca/utils/cli_parsing.rb +82 -0
  14. data/lib/puppetserver/ca/utils/config.rb +13 -0
  15. data/lib/puppetserver/ca/utils/file_system.rb +90 -0
  16. data/lib/puppetserver/ca/utils/http_client.rb +129 -0
  17. data/lib/puppetserver/ca/utils/signing_digest.rb +27 -0
  18. data/lib/puppetserver/ca/version.rb +1 -1
  19. metadata +17 -17
  20. data/lib/puppetserver/ca/clean_action.rb +0 -157
  21. data/lib/puppetserver/ca/config_utils.rb +0 -11
  22. data/lib/puppetserver/ca/create_action.rb +0 -265
  23. data/lib/puppetserver/ca/generate_action.rb +0 -227
  24. data/lib/puppetserver/ca/import_action.rb +0 -153
  25. data/lib/puppetserver/ca/list_action.rb +0 -153
  26. data/lib/puppetserver/ca/puppet_config.rb +0 -197
  27. data/lib/puppetserver/ca/puppetserver_config.rb +0 -83
  28. data/lib/puppetserver/ca/revoke_action.rb +0 -136
  29. data/lib/puppetserver/ca/sign_action.rb +0 -190
  30. data/lib/puppetserver/ca/utils.rb +0 -80
  31. data/lib/puppetserver/settings/ttl_setting.rb +0 -48
  32. data/lib/puppetserver/utils/file_utilities.rb +0 -78
  33. data/lib/puppetserver/utils/http_client.rb +0 -129
  34. data/lib/puppetserver/utils/signing_digest.rb +0 -25
@@ -1,157 +0,0 @@
1
- require 'puppetserver/ca/utils'
2
- require 'puppetserver/utils/http_client'
3
- require 'puppetserver/utils/file_utilities'
4
- require 'puppetserver/ca/puppet_config'
5
- require 'puppetserver/ca/revoke_action'
6
-
7
- require 'optparse'
8
- require 'json'
9
-
10
- module Puppetserver
11
- module Ca
12
- class CleanAction
13
-
14
- include Puppetserver::Utils
15
-
16
- CERTNAME_BLACKLIST = %w{--all --config}
17
-
18
- SUMMARY = 'Clean files from the CA for certificate(s)'
19
- BANNER = <<-BANNER
20
- Usage:
21
- puppetserver ca clean [--help|--version]
22
- puppetserver ca clean [--config] --certname CERTNAME[,ADDLCERTNAME]
23
-
24
- Description:
25
- Given one or more valid certnames, instructs the CA to revoke certificates
26
- matching the given certnames if they exist, and then remove files pertaining
27
- to them (keys, cert, and certificate request) over HTTPS using the local
28
- agent's PKI
29
-
30
- Options:
31
- BANNER
32
-
33
- def self.parser(parsed = {})
34
- parsed['certnames'] = []
35
- OptionParser.new do |o|
36
- o.banner = BANNER
37
- o.on('--certname foo,bar', Array,
38
- 'One or more comma separated certnames') do |certs|
39
- parsed['certnames'] += certs
40
- end
41
- o.on('--config PUPPET.CONF', 'Custom path to puppet.conf') do |conf|
42
- parsed['config'] = conf
43
- end
44
- o.on('--help', 'Displays this clean specific help output') do |help|
45
- parsed['help'] = help
46
- end
47
- end
48
- end
49
-
50
- def initialize(logger)
51
- @logger = logger
52
- end
53
-
54
- def parse(args)
55
- results = {}
56
- parser = self.class.parser(results)
57
-
58
- errors = Utils.parse_with_errors(parser, args)
59
-
60
- results['certnames'].each do |certname|
61
- if CERTNAME_BLACKLIST.include?(certname)
62
- errors << " Cannot manage cert named `#{certname}` from " +
63
- "the CLI, if needed use the HTTP API directly"
64
- end
65
- end
66
-
67
- if results['certnames'].empty?
68
- errors << ' At least one certname is required to clean'
69
- end
70
-
71
- errors_were_handled = Utils.handle_errors(@logger, errors, parser.help)
72
-
73
- exit_code = errors_were_handled ? 1 : nil
74
-
75
- return results, exit_code
76
- end
77
-
78
- def run(args)
79
- certnames = args['certnames']
80
- config = args['config']
81
-
82
- if config
83
- errors = FileUtilities.validate_file_paths(config)
84
- return 1 if Utils.handle_errors(@logger, errors)
85
- end
86
-
87
- puppet = PuppetConfig.parse(config)
88
- return 1 if Utils.handle_errors(@logger, puppet.errors)
89
-
90
- passed = clean_certs(certnames, puppet.settings)
91
-
92
- return passed ? 0 : 1
93
- end
94
-
95
- def clean_certs(certnames, settings)
96
- client = HttpClient.new(settings)
97
-
98
- url = client.make_ca_url(settings[:ca_server],
99
- settings[:ca_port],
100
- 'certificate_status')
101
-
102
- results = client.with_connection(url) do |connection|
103
- certnames.map do |certname|
104
- url.resource_name = certname
105
- revoke_result = connection.put(RevokeAction::REQUEST_BODY, url)
106
- revoked = check_revocation(revoke_result, certname)
107
-
108
- cleaned = nil
109
- unless revoked == :error
110
- clean_result = connection.delete(url)
111
- cleaned = check_result(clean_result, certname)
112
- end
113
-
114
- cleaned == :success && [:success, :not_found].include?(revoked)
115
- end
116
- end
117
-
118
- return results.all?
119
- end
120
-
121
- # possibly logs the action, always returns a status symbol 👑
122
- def check_revocation(result, certname)
123
- case result.code
124
- when '200', '204'
125
- @logger.inform "Revoked certificate for #{certname}"
126
- return :success
127
- when '404'
128
- return :not_found
129
- else
130
- @logger.err 'Error:'
131
- @logger.err " Failed revoking certificate for #{certname}"
132
- @logger.err " Received code: #{result.code}, body: #{result.body}"
133
- return :error
134
- end
135
- end
136
-
137
- # logs the action and returns a status symbol 👑
138
- def check_result(result, certname)
139
- case result.code
140
- when '200', '204'
141
- @logger.inform "Cleaned files related to #{certname}"
142
- return :success
143
- when '404'
144
- @logger.err 'Error:'
145
- @logger.err " Could not find files for #{certname}"
146
- return :not_found
147
- else
148
- @logger.err 'Error:'
149
- @logger.err " When cleaning #{certname} received:"
150
- @logger.err " code: #{result.code}"
151
- @logger.err " body: #{result.body.to_s}" if result.body
152
- return :error
153
- end
154
- end
155
- end
156
- end
157
- end
@@ -1,11 +0,0 @@
1
- module Puppetserver
2
- module Ca
3
- module ConfigUtils
4
-
5
- def running_as_root?
6
- !Gem.win_platform? && Process::UID.eid == 0
7
- end
8
-
9
- end
10
- end
11
- end
@@ -1,265 +0,0 @@
1
- require 'puppetserver/ca/utils'
2
- require 'puppetserver/ca/host'
3
- require 'puppetserver/ca/puppet_config'
4
- require 'puppetserver/utils/file_utilities'
5
- require 'puppetserver/utils/http_client'
6
- require 'puppetserver/utils/signing_digest'
7
- require 'json'
8
-
9
- module Puppetserver
10
- module Ca
11
- class CreateAction
12
-
13
- include Puppetserver::Utils
14
-
15
- # Only allow printing ascii characters, excluding /
16
- VALID_CERTNAME = /\A[ -.0-~]+\Z/
17
- CERTNAME_BLACKLIST = %w{--all --config}
18
-
19
- SUMMARY = "Create a new certificate signed by the CA"
20
- BANNER = <<-BANNER
21
- Usage:
22
- puppetserver ca create [--help]
23
- puppetserver ca create [--config] --certname CERTNAME[,ADDLCERTNAME]
24
-
25
- Description:
26
- Creates a new certificate signed by the intermediate CA
27
- and stores generated keys and certs on disk.
28
-
29
- To determine the target location, the default puppet.conf
30
- is consulted for custom values. If using a custom puppet.conf
31
- provide it with the --config flag
32
-
33
- Options:
34
- BANNER
35
- def initialize(logger)
36
- @logger = logger
37
- end
38
-
39
- def self.parser(parsed = {})
40
- parsed['certnames'] = []
41
- OptionParser.new do |opts|
42
- opts.banner = BANNER
43
- opts.on('--certname FOO,BAR', Array,
44
- 'One or more comma separated certnames') do |certs|
45
- parsed['certnames'] += certs
46
- end
47
- opts.on('--help', 'Display this create specific help output') do |help|
48
- parsed['help'] = true
49
- end
50
- opts.on('--config CONF', 'Path to puppet.conf') do |conf|
51
- parsed['config'] = conf
52
- end
53
- end
54
- end
55
-
56
- def parse(args)
57
- results = {}
58
- parser = self.class.parser(results)
59
-
60
- errors = Utils.parse_with_errors(parser, args)
61
-
62
- if results['certnames'].empty?
63
- errors << ' At least one certname is required to create'
64
- else
65
- results['certnames'].each do |certname|
66
- if CERTNAME_BLACKLIST.include?(certname)
67
- errors << " Cannot manage cert named `#{certname}` from " +
68
- "the CLI, if needed use the HTTP API directly"
69
- end
70
-
71
- if certname.match(/\p{Upper}/)
72
- errors << " Certificate names must be lower case"
73
- end
74
-
75
- unless certname =~ VALID_CERTNAME
76
- errors << " Certname #{certname} must not contain unprintable or non-ASCII characters"
77
- end
78
- end
79
- end
80
-
81
- errors_were_handled = Utils.handle_errors(@logger, errors, parser.help)
82
-
83
- exit_code = errors_were_handled ? 1 : nil
84
-
85
- return results, exit_code
86
- end
87
-
88
- def run(input)
89
- certnames = input['certnames']
90
- config_path = input['config']
91
-
92
- # Validate config_path provided
93
- if config_path
94
- errors = FileUtilities.validate_file_paths(config_path)
95
- return 1 if Utils.handle_errors(@logger, errors)
96
- end
97
-
98
- # Load, resolve, and validate puppet config settings
99
- puppet = PuppetConfig.parse(config_path)
100
- return 1 if Utils.handle_errors(@logger, puppet.errors)
101
-
102
- # Load most secure signing digest we can for csr signing.
103
- signer = SigningDigest.new
104
- return 1 if Utils.handle_errors(@logger, signer.errors)
105
-
106
- # Make sure we have all the directories where we will be writing files
107
- FileUtilities.ensure_dir(puppet.settings[:certdir])
108
- FileUtilities.ensure_dir(puppet.settings[:privatekeydir])
109
- FileUtilities.ensure_dir(puppet.settings[:publickeydir])
110
-
111
- # Generate and save certs and associated keys
112
- all_passed = generate_certs(certnames, puppet.settings, signer.digest)
113
- return all_passed ? 0 : 1
114
- end
115
-
116
- # Create csrs and keys, then submit them to CA, request for the CA to sign
117
- # them, download the signed certificates from the CA, and finally save
118
- # the signed certs and associated keys. Returns true if all certs were
119
- # successfully created and saved.
120
- def generate_certs(certnames, settings, digest)
121
- passed = certnames.map do |certname|
122
- key, csr = generate_key_csr(certname, settings, digest)
123
- return false unless submit_certificate_request(certname, csr.to_s, settings)
124
- return false unless sign_cert(certname, settings)
125
- if download_cert(certname, settings)
126
- save_keys(key, certname, settings)
127
- true
128
- else
129
- false
130
- end
131
- end
132
- passed.all?
133
- end
134
-
135
- def generate_key_csr(certname, settings, digest)
136
- host = Puppetserver::Ca::Host.new(digest)
137
- private_key = host.create_private_key(settings[:keylength])
138
- csr = host.create_csr(certname, private_key)
139
-
140
- return private_key, csr
141
- end
142
-
143
- def http_client(settings)
144
- @client ||= HttpClient.new(settings)
145
- end
146
-
147
- # Make an HTTP request to submit certificate requests to CA
148
- # @param certname [String] the name of the certificate to fetch
149
- # @param csr [String] string version of a OpenSSL::X509::Request
150
- # @param settings [Hash] a hash of config settings
151
- # @return [Boolean] success of all csrs being submitted to CA
152
- def submit_certificate_request(certname, csr, settings)
153
- client = http_client(settings)
154
- url = client.make_ca_url(settings[:ca_server],
155
- settings[:ca_port],
156
- 'certificate_request',
157
- certname)
158
-
159
- client.with_connection(url) do |connection|
160
- result = connection.put(csr, url)
161
- check_submit_result(result, certname)
162
- end
163
- end
164
-
165
- def check_submit_result(result, certname)
166
- case result.code
167
- when '200', '204'
168
- @logger.inform "Successfully submitted certificate request for #{certname}"
169
- return true
170
- else
171
- @logger.err 'Error:'
172
- @logger.err " When certificate request submitted for #{certname}:"
173
- @logger.err " code: #{result.code}"
174
- @logger.err " body: #{result.body.to_s}" if result.body
175
- return false
176
- end
177
- end
178
-
179
- # Make an HTTP request to CA to sign the named certificates
180
- # @param certname [String] the name of the certificate to have signed
181
- # @param settings [Hash] a hash of config settings
182
- # @return [Boolean] the success of certificates being signed
183
- def sign_cert(certname, settings)
184
- client = http_client(settings)
185
-
186
- url = client.make_ca_url(settings[:ca_server],
187
- settings[:ca_port],
188
- 'certificate_status',
189
- certname)
190
-
191
- client.with_connection(url) do |connection|
192
- body = JSON.dump({desired_state: 'signed'})
193
- result = connection.put(body, url)
194
- check_sign_result(result, certname)
195
- end
196
- end
197
-
198
- def check_sign_result(result, certname)
199
- case result.code
200
- when '204'
201
- @logger.inform "Successfully signed certificate request for #{certname}"
202
- return true
203
- else
204
- @logger.err 'Error:'
205
- @logger.err " When signing request submitted for #{certname}:"
206
- @logger.err " code: #{result.code}"
207
- @logger.err " body: #{result.body.to_s}" if result.body
208
- return false
209
- end
210
- end
211
-
212
- # Make an HTTP request to fetch the named certificates from CA
213
- # @param certname [String] the name of the certificate to fetch
214
- # @param settings [Hash] a hash of config settings
215
- # @return [Boolean] the success of certificate being downloaded
216
- def download_cert(certname, settings)
217
- client = http_client(settings)
218
- url = client.make_ca_url(settings[:ca_server],
219
- settings[:ca_port],
220
- 'certificate',
221
- certname)
222
- client.with_connection(url) do |connection|
223
- result = connection.get(url)
224
- if downloaded = check_download_result(result, certname)
225
- save_file(result.body, certname, settings[:certdir], "Certificate")
226
- @logger.inform "Successfully downloaded and saved certificate #{certname} to #{settings[:certdir]}/#{certname}.pem"
227
- end
228
- downloaded
229
- end
230
- end
231
-
232
- def check_download_result(result, certname)
233
- case result.code
234
- when '200'
235
- return true
236
- when '404'
237
- @logger.err 'Error:'
238
- @logger.err " Signed certificate #{certname} could not be found on the CA"
239
- return false
240
- else
241
- @logger.err 'Error:'
242
- @logger.err " When download requested for certificate #{certname}:"
243
- @logger.err " code: #{result.code}"
244
- @logger.err " body: #{result.body.to_s}" if result.body
245
- return false
246
- end
247
- end
248
-
249
- def save_keys(key, certname, settings)
250
- public_key = key.public_key
251
- save_file(key, certname, settings[:privatekeydir], "Private key")
252
- save_file(public_key, certname, settings[:publickeydir], "Public key")
253
- @logger.inform "Successfully saved private key for #{certname} to #{settings[:privatekeydir]}/#{certname}.pem"
254
- @logger.inform "Successfully saved public key for #{certname} to #{settings[:publickeydir]}/#{certname}.pem"
255
- end
256
-
257
-
258
- def save_file(content, certname, dir, type)
259
- location = File.join(dir, "#{certname}.pem")
260
- @logger.warn "#{type} #{certname}.pem already exists, overwriting" if File.exist?(location)
261
- FileUtilities.write_file(location, content, 0640)
262
- end
263
- end
264
- end
265
- end
@@ -1,227 +0,0 @@
1
- require 'optparse'
2
- require 'openssl'
3
- require 'puppetserver/utils/file_utilities'
4
- require 'puppetserver/ca/host'
5
- require 'puppetserver/utils/signing_digest'
6
-
7
- module Puppetserver
8
- module Ca
9
- class GenerateAction
10
- include Puppetserver::Utils
11
-
12
- CA_EXTENSIONS = [
13
- ["basicConstraints", "CA:TRUE", true],
14
- ["keyUsage", "keyCertSign, cRLSign", true],
15
- ["subjectKeyIdentifier", "hash", false],
16
- ["authorityKeyIdentifier", "keyid:always", false]
17
- ].freeze
18
-
19
- # Make the certificate valid as of yesterday, because so many people's
20
- # clocks are out of sync. This gives one more day of validity than people
21
- # might expect, but is better than making every person who has a messed up
22
- # clock fail, and better than having every cert we generate expire a day
23
- # before the user expected it to when they asked for "one year".
24
- CERT_VALID_FROM = (Time.now - (60*60*24)).freeze
25
-
26
- SUMMARY = "Generate a root and intermediate signing CA for Puppet Server"
27
- BANNER = <<-BANNER
28
- Usage:
29
- puppetserver ca generate [--help]
30
- puppetserver ca generate [--config PATH]
31
-
32
- Description:
33
- Generate a root and intermediate signing CA for Puppet Server
34
- and store generated CA keys, certs, and crls on disk.
35
-
36
- To determine the target location, the default puppet.conf
37
- is consulted for custom values. If using a custom puppet.conf
38
- provide it with the --config flag
39
-
40
- Options:
41
- BANNER
42
-
43
- def initialize(logger)
44
- @logger = logger
45
- end
46
-
47
- def run(input)
48
- # Validate config_path provided
49
- config_path = input['config']
50
- if config_path
51
- errors = FileUtilities.validate_file_paths(config_path)
52
- return 1 if Utils.handle_errors(@logger, errors)
53
- end
54
-
55
- # Load, resolve, and validate puppet config settings
56
- puppet = PuppetConfig.parse(config_path)
57
- return 1 if Utils.handle_errors(@logger, puppet.errors)
58
-
59
- # Load most secure signing digest we can for cers/crl/csr signing.
60
- signer = SigningDigest.new
61
- return 1 if Utils.handle_errors(@logger, signer.errors)
62
-
63
- # Generate root and intermediate ca and put all the certificates, crls,
64
- # and keys where they should go.
65
- generate_root_and_intermediate_ca(puppet.settings, signer.digest)
66
-
67
- # Puppet's internal CA expects these file to exist.
68
- FileUtilities.ensure_file(puppet.settings[:serial], "001", 0640)
69
- FileUtilities.ensure_file(puppet.settings[:cert_inventory], "", 0640)
70
-
71
- @logger.inform "Generation succeeded. Find your files in #{puppet.settings[:cadir]}"
72
- return 0
73
- end
74
-
75
- def generate_root_and_intermediate_ca(settings, signing_digest)
76
- valid_until = Time.now + settings[:ca_ttl]
77
- host = Puppetserver::Ca::Host.new(signing_digest)
78
-
79
- root_key = host.create_private_key(settings[:keylength])
80
- root_cert = self_signed_ca(root_key, settings[:root_ca_name], valid_until, signing_digest)
81
- root_crl = create_crl_for(root_cert, root_key, valid_until, signing_digest)
82
-
83
- int_key = host.create_private_key(settings[:keylength])
84
- int_csr = host.create_csr(settings[:ca_name], int_key)
85
- int_cert = sign_intermediate(root_key, root_cert, int_csr, valid_until, signing_digest)
86
- int_crl = create_crl_for(int_cert, int_key, valid_until, signing_digest)
87
-
88
- FileUtilities.ensure_dir(settings[:cadir])
89
-
90
- file_properties = [
91
- [settings[:cacert], [int_cert, root_cert]],
92
- [settings[:cakey], int_key],
93
- [settings[:rootkey], root_key],
94
- [settings[:cacrl], [int_crl, root_crl]]
95
- ]
96
-
97
- file_properties.each do |location, content|
98
- @logger.warn "#{location} exists, overwriting" if File.exist?(location)
99
- FileUtilities.write_file(location, content, 0640)
100
- end
101
- end
102
-
103
- def self_signed_ca(key, name, valid_until, signing_digest)
104
- cert = OpenSSL::X509::Certificate.new
105
-
106
- cert.public_key = key.public_key
107
- cert.subject = OpenSSL::X509::Name.new([["CN", name]])
108
- cert.issuer = cert.subject
109
- cert.version = 2
110
- cert.serial = 1
111
-
112
- cert.not_before = CERT_VALID_FROM
113
- cert.not_after = valid_until
114
-
115
- ef = extension_factory_for(cert, cert)
116
- CA_EXTENSIONS.each do |ext|
117
- extension = ef.create_extension(*ext)
118
- cert.add_extension(extension)
119
- end
120
-
121
- cert.sign(key, signing_digest)
122
-
123
- cert
124
- end
125
-
126
- def extension_factory_for(ca, cert = nil)
127
- ef = OpenSSL::X509::ExtensionFactory.new
128
- ef.issuer_certificate = ca
129
- ef.subject_certificate = cert if cert
130
-
131
- ef
132
- end
133
-
134
- def create_crl_for(ca_cert, ca_key, valid_until, signing_digest)
135
- crl = OpenSSL::X509::CRL.new
136
- crl.version = 1
137
- crl.issuer = ca_cert.subject
138
-
139
- ef = extension_factory_for(ca_cert)
140
- crl.add_extension(
141
- ef.create_extension(["authorityKeyIdentifier", "keyid:always", false]))
142
- crl.add_extension(
143
- OpenSSL::X509::Extension.new("crlNumber", OpenSSL::ASN1::Integer(0)))
144
-
145
- crl.last_update = CERT_VALID_FROM
146
- crl.next_update = valid_until
147
- crl.sign(ca_key, signing_digest)
148
-
149
- crl
150
- end
151
-
152
- def sign_intermediate(ca_key, ca_cert, csr, valid_until, signing_digest)
153
- cert = OpenSSL::X509::Certificate.new
154
-
155
- cert.public_key = csr.public_key
156
- cert.subject = csr.subject
157
- cert.issuer = ca_cert.subject
158
- cert.version = 2
159
- cert.serial = 2
160
-
161
- cert.not_before = CERT_VALID_FROM
162
- cert.not_after = valid_until
163
-
164
- ef = extension_factory_for(ca_cert, cert)
165
- CA_EXTENSIONS.each do |ext|
166
- extension = ef.create_extension(*ext)
167
- cert.add_extension(extension)
168
- end
169
- cert.sign(ca_key, signing_digest)
170
-
171
- cert
172
- end
173
-
174
- def parse(cli_args)
175
- parser, inputs, unparsed = parse_inputs(cli_args)
176
-
177
- if !unparsed.empty?
178
- @logger.err 'Error:'
179
- @logger.err 'Unknown arguments or flags:'
180
- unparsed.each do |arg|
181
- @logger.err " #{arg}"
182
- end
183
-
184
- @logger.err ''
185
- @logger.err parser.help
186
-
187
- exit_code = 1
188
- else
189
- exit_code = nil
190
- end
191
-
192
- return inputs, exit_code
193
- end
194
-
195
- def parse_inputs(inputs)
196
- parsed = {}
197
- unparsed = []
198
-
199
- parser = self.class.parser(parsed)
200
-
201
- begin
202
- parser.order!(inputs) do |nonopt|
203
- unparsed << nonopt
204
- end
205
- rescue OptionParser::ParseError => e
206
- unparsed += e.args
207
- unparsed << inputs.shift unless inputs.first =~ /^-{1,2}/
208
- retry
209
- end
210
-
211
- return parser, parsed, unparsed
212
- end
213
-
214
- def self.parser(parsed = {})
215
- OptionParser.new do |opts|
216
- opts.banner = BANNER
217
- opts.on('--help', 'Display this generate specific help output') do |help|
218
- parsed['help'] = true
219
- end
220
- opts.on('--config CONF', 'Path to puppet.conf') do |conf|
221
- parsed['config'] = conf
222
- end
223
- end
224
- end
225
- end
226
- end
227
- end