ncedit 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/README.md +111 -16
- data/doc/example/batch.json +34 -0
- data/{batch.yaml → doc/example/batch.yaml} +12 -11
- data/exe/ncedit +13 -10
- data/lib/ncedit/cmd.rb +100 -42
- data/lib/ncedit/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 326ad90c579b6ab122bc1aca4e92f2ba9409f556
|
4
|
+
data.tar.gz: 58fb80c7453318166436e8fac0d84a3d57b52c56
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a3053204bc285ae301a4848391e7bf52d46baf3feb4c40c8beac48998710df92888927e4b1fe4072affc4415823b28874bb9cdac81f8631765de60a44567a9ef
|
7
|
+
data.tar.gz: 0d0c2c813e97aad9b2d41a1bf3ae8c9cce8b34ac310c3b8f3ab0b1e98b2ac4a1f8148ac45f8394e21e46e6fdbac4c85d0f17beb24accb7ddd25db3792dd388cb
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,36 +1,131 @@
|
|
1
1
|
[![Build Status](https://travis-ci.org/GeoffWilliams/ncedit.svg?branch=master)](https://travis-ci.org/GeoffWilliams/ncedit)
|
2
|
-
#
|
2
|
+
# ncedit - Puppet Node Classifier CLI editor
|
3
3
|
|
4
|
-
|
4
|
+
ncedit is a small utility program that lets you edit the Puppet Enterprise Node Classifier rules from the commandline.
|
5
5
|
|
6
|
-
|
6
|
+
Why would you want to do this given that we have the excellent [node_manager] (https://forge.puppet.com/WhatsARanjit/node_manager) module already on the forge? Well... lots of reasons. First off, using puppet code to drive the Node Classifier means that you have to have the `node_manager` module alread installed, which means that you must already have your [classification rules](https://docs.puppet.com/pe/latest/console_classes_groups_getting_started.html) in to reference the module through [Code Manager](https://docs.puppet.com/pe/latest/code_mgr.html). You could in-theory use Puppet Enterprise's new idempotent installer (just reinstall puppet over the top of itself) to fix this exact issue but then you still have the problem of how to classify your master in order to activate any other new rules (eg node-ttl) you want to use, which are associated with a new [role](https://docs.puppet.com/pe/latest/r_n_p_intro.html) for the Puppet Master.
|
7
|
+
|
8
|
+
That's where this tool comes in since all you need is root shell on the Puppet Master and a YAML or JSON file with the changes you want to make...
|
9
|
+
|
10
|
+
## Features
|
11
|
+
* Create node groups
|
12
|
+
* Add or remove classes
|
13
|
+
* Add or remove class parameters
|
14
|
+
* Add or remove rules
|
15
|
+
|
16
|
+
All from the convenience of the CLI. This also allows this tool to be called from scripts and other systems in order to setup Puppet Enterprise the way you want and with the minimum of effort.
|
17
|
+
|
18
|
+
For the moment, the edits to be carried out need to be placed into either a JSON or YAML file for bulk processing. If there is interest, the tool will be enhanced to allow the above operations to be specified individually on the command line so to avoid the need to write YAML/JSON.
|
7
19
|
|
8
20
|
## Installation
|
21
|
+
Install this tool on your Puppet Master (Master-of-Masters)
|
9
22
|
|
10
|
-
|
23
|
+
```shell
|
24
|
+
$ sudo /opt/puppetlabs/puppet/bin/gem install ncedit
|
25
|
+
```
|
26
|
+
This will install the gem into the Puppet Master's vendored ruby. You could instead use your OS ruby and it should work, just make sure you have a recent-ish version of ruby.
|
27
|
+
|
28
|
+
## Usage
|
11
29
|
|
12
|
-
|
13
|
-
|
30
|
+
### Node Classifier API access
|
31
|
+
ncedit is intended to be run on the Puppet Master as root. Doing so avoids having to deal with networks, firewalls, whitelists and the Puppet Enterprise RBAC API. While it would be cool to expand the tool to deal with these issues, its just a whole lot simpler to just run from the Puppet Master so right now that's all thats supported.
|
32
|
+
|
33
|
+
### Making batch changes
|
34
|
+
To avoid the need to repeatedly invoke this tool using say, a bash script, ncedit natively supports reading a file of batch changes to make that is written in either YAML or JSON.
|
35
|
+
|
36
|
+
#### Batch data file
|
37
|
+
* Since ncedit internally represents all from the Node Classifier API as hashes, its easy to support both YAML and JSON since they both resolve to this format
|
38
|
+
* It's possible to add multiple groups at a time, you just need another `NAME_OF_GROUP` stanza
|
39
|
+
|
40
|
+
In each case the file needs to be ordered as follows:
|
41
|
+
|
42
|
+
##### YAML
|
43
|
+
```yaml
|
44
|
+
"NAME_OF_GROUP":
|
45
|
+
# hash of classes to edit/create
|
46
|
+
"classes":
|
47
|
+
"CLASS_TO_ENSURE":
|
48
|
+
"OPTIONAL_PARAM_NAME": "VALUE_TO_SET"
|
49
|
+
# Array of classes to delete
|
50
|
+
"delete_classes":
|
51
|
+
- "CLASS_TO_DELETE"
|
52
|
+
# Hash classes + Array of parameter names to delete
|
53
|
+
"delete_params":
|
54
|
+
"CLASS_TO_PROCESS":
|
55
|
+
- "PARAMETER_TO_DELETE"
|
56
|
+
# Rules to append to group
|
57
|
+
"append_rules":
|
58
|
+
- "CONDITIONAL" # 'and'/'or'
|
59
|
+
- - "=" # rule tuple 0 - comparator, eg '='
|
60
|
+
- "VARIABLE" # rule tuple 1 - variable, eg 'fqdn'
|
61
|
+
- "VALUE" # rule tuple 2 - value to match, eg 'pupper.puppet.com'
|
14
62
|
```
|
15
63
|
|
16
|
-
|
64
|
+
* [Worked example](doc/example/batch.yaml)
|
65
|
+
|
66
|
+
##### JSON
|
67
|
+
```json
|
68
|
+
{
|
69
|
+
"NAME_OF_GROUP": {
|
70
|
+
"classes": {
|
71
|
+
"CLASS_TO_ENSURE": {
|
72
|
+
"OPTIONAL_PARAM_NAME": "VALUE_TO_SET"
|
73
|
+
}
|
74
|
+
},
|
75
|
+
"delete_classes": [
|
76
|
+
"CLASS_TO_DELETE"
|
77
|
+
],
|
78
|
+
"delete_params": {
|
79
|
+
"CLASS_TO_PROCESS": [
|
80
|
+
"PARAMETER_TO_DELETE"
|
81
|
+
]
|
82
|
+
},
|
83
|
+
"append_rules": [
|
84
|
+
"CONDITIONAL",
|
85
|
+
[
|
86
|
+
"=",
|
87
|
+
"VARIABLE",
|
88
|
+
"VALUE"
|
89
|
+
]
|
90
|
+
]
|
91
|
+
}
|
92
|
+
}
|
93
|
+
```
|
17
94
|
|
18
|
-
|
95
|
+
* JSON doesn't support comments natively so please see above YAML example for notes
|
96
|
+
* [Worked example](doc/example/batch.json)
|
19
97
|
|
20
|
-
|
98
|
+
#### Ensuring changes
|
99
|
+
* ncedit is idempotent so you may run the command as often as you like
|
21
100
|
|
22
|
-
|
101
|
+
##### YAML
|
102
|
+
```shell
|
103
|
+
ncedit batch --yaml-file /path/to/batch.yaml
|
104
|
+
```
|
23
105
|
|
24
|
-
|
106
|
+
##### JSON
|
107
|
+
```shell
|
108
|
+
ncedit batch --json-file /path/to/batch.json
|
109
|
+
```
|
25
110
|
|
26
|
-
|
111
|
+
## Making per-item changes
|
112
|
+
* coming? (soon?) -- anyone want this?
|
27
113
|
|
28
|
-
##
|
114
|
+
## Troubleshooting
|
115
|
+
* If the ncedit fails, it will swallow stack traces by default, pass the `--verbosity debug` argument if you need to obtain one. Note the position of the argument before the command name:
|
116
|
+
```
|
117
|
+
bundle exec ncedit --verbosity debug batch --yaml-file /path/to/batch.yaml
|
118
|
+
```
|
119
|
+
* Ensure you are running as `root` to avoid permission errors
|
29
120
|
|
30
|
-
|
121
|
+
## Testing
|
122
|
+
To run tests:
|
31
123
|
|
32
|
-
|
124
|
+
```shell
|
125
|
+
bundle install
|
126
|
+
bundle exec rake spec
|
127
|
+
```
|
33
128
|
|
34
129
|
## Contributing
|
35
130
|
|
36
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
131
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/GeoffWilliams/ncedit.
|
@@ -0,0 +1,34 @@
|
|
1
|
+
{
|
2
|
+
"PE Master": {
|
3
|
+
"classes": {
|
4
|
+
"puppet_enterprise::profile::master": {
|
5
|
+
"code_manager_auto_configure": true,
|
6
|
+
|
7
|
+
"r10k_remote": "https://github.com/GeoffWilliams/r10k-control",
|
8
|
+
"r10k_private_key": "/etc/puppetlabs/puppetserver/ssh/id-control_repo.rsa"
|
9
|
+
}
|
10
|
+
},
|
11
|
+
"delete_classes": [
|
12
|
+
"puppet_enterprise::profile::masterbad"
|
13
|
+
],
|
14
|
+
"delete_params": {
|
15
|
+
"puppet_enterprise::profile::redo:": [
|
16
|
+
"badparam"
|
17
|
+
]
|
18
|
+
}
|
19
|
+
},
|
20
|
+
"Puppet Masters": {
|
21
|
+
"classes": {
|
22
|
+
"r_role::puppet::master_minimal": {
|
23
|
+
}
|
24
|
+
},
|
25
|
+
"append_rules": [
|
26
|
+
"or",
|
27
|
+
[
|
28
|
+
"=",
|
29
|
+
"name",
|
30
|
+
"pupper.puppet.com"
|
31
|
+
]
|
32
|
+
]
|
33
|
+
}
|
34
|
+
}
|
@@ -9,18 +9,19 @@
|
|
9
9
|
"code_manager_auto_configure": true
|
10
10
|
"r10k_remote": "https://github.com/GeoffWilliams/r10k-control"
|
11
11
|
"r10k_private_key": "/etc/puppetlabs/puppetserver/ssh/id-control_repo.rsa"
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
12
|
+
|
13
|
+
"delete_classes":
|
14
|
+
- "puppet_enterprise::profile::masterbad"
|
15
|
+
|
16
|
+
"delete_params":
|
17
|
+
"puppet_enterprise::profile::redo:":
|
18
|
+
- "badparam"
|
19
19
|
|
20
20
|
"Puppet Masters":
|
21
21
|
"classes":
|
22
22
|
"r_role::puppet::master_minimal": {}
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
"append_rules":
|
24
|
+
- "or"
|
25
|
+
- - "="
|
26
|
+
- "name"
|
27
|
+
- "pupper.puppet.com"
|
data/exe/ncedit
CHANGED
@@ -56,22 +56,25 @@ Escort::App.create do |app|
|
|
56
56
|
# end
|
57
57
|
|
58
58
|
app.command :batch do |command|
|
59
|
-
command.summary "Batch processing from YAML"
|
60
|
-
command.description "Process a YAML file to add/delete classes, parameters and rules"
|
59
|
+
command.summary "Batch processing from YAML/JSON"
|
60
|
+
command.description "Process a YAML/JSON file to add/delete classes, parameters and rules"
|
61
61
|
command.options do |opts|
|
62
|
-
opts.opt(:
|
62
|
+
opts.opt(:yaml_file,
|
63
63
|
'YAML file',
|
64
|
-
:long => '--
|
64
|
+
:long => '--yaml-file',
|
65
|
+
:type => :string,
|
66
|
+
)
|
67
|
+
opts.opt(:json_file,
|
68
|
+
'JSON file',
|
69
|
+
:long => '--json-file',
|
65
70
|
:type => :string,
|
66
71
|
)
|
67
72
|
end
|
68
73
|
command.action do |options, arguments|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
NCEdit::Cmd::batch(filename)
|
74
|
-
end
|
74
|
+
yaml_file = options[:global][:commands][:batch][:options][:yaml_file]
|
75
|
+
json_file = options[:global][:commands][:batch][:options][:json_file]
|
76
|
+
|
77
|
+
NCEdit::Cmd::batch(yaml_file: yaml_file, json_file: json_file)
|
75
78
|
end
|
76
79
|
end
|
77
80
|
end
|
data/lib/ncedit/cmd.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
require 'puppetclassify'
|
2
2
|
require 'yaml'
|
3
|
+
require 'json'
|
3
4
|
require 'escort'
|
4
5
|
|
5
6
|
module NCEdit
|
6
7
|
module Cmd
|
8
|
+
DEFAULT_RULE = "or"
|
7
9
|
|
8
10
|
def self.init(puppetclassify = nil)
|
9
11
|
if puppetclassify
|
@@ -133,6 +135,35 @@ module NCEdit
|
|
133
135
|
end
|
134
136
|
end
|
135
137
|
|
138
|
+
def self.read_batch_data(yaml_file: nil, json_file:nil)
|
139
|
+
if yaml_file == nil and json_file == nil
|
140
|
+
raise "YAML or JSON file must be specified for batch updates"
|
141
|
+
elsif yaml_file and json_file
|
142
|
+
raise "Cannot process both YAML and JSON at the same time"
|
143
|
+
elsif yaml_file
|
144
|
+
if File.exists?(yaml_file)
|
145
|
+
begin
|
146
|
+
data = YAML.load_file(yaml_file)
|
147
|
+
rescue Psych::SyntaxError
|
148
|
+
raise "syntax error parsing #{yaml_file}"
|
149
|
+
end
|
150
|
+
else
|
151
|
+
raise "YAML file not found: #{yaml_file}"
|
152
|
+
end
|
153
|
+
elsif json_file
|
154
|
+
if File.exists?(json_file)
|
155
|
+
begin
|
156
|
+
data = JSON.parse(IO.read(json_file))
|
157
|
+
rescue JSON::ParserError
|
158
|
+
raise "syntax error parsing #{json_file}"
|
159
|
+
end
|
160
|
+
else
|
161
|
+
raise "JSON file not found: #{json_file}"
|
162
|
+
end
|
163
|
+
end
|
164
|
+
data
|
165
|
+
end
|
166
|
+
|
136
167
|
# Batch entry from YAML file, example file format:
|
137
168
|
# 'PE Master':
|
138
169
|
# 'classes':
|
@@ -154,44 +185,36 @@ module NCEdit
|
|
154
185
|
# - - "="
|
155
186
|
# - "name"
|
156
187
|
# - "vmpump02.puppet.com"
|
157
|
-
def self.batch(
|
158
|
-
|
159
|
-
|
160
|
-
yaml = YAML.load_file(filename)
|
161
|
-
|
162
|
-
yaml.each { |group_name, data|
|
163
|
-
Escort::Logger.output.puts "Processing #{group_name}"
|
164
|
-
|
165
|
-
if data.has_key?("delete_classes")
|
166
|
-
if delete_classes(group_name, data["delete_classes"])
|
167
|
-
update_group(group_name, classes: data["delete_classes"])
|
168
|
-
end
|
169
|
-
end
|
188
|
+
def self.batch(yaml_file: nil, json_file: nil)
|
189
|
+
data = read_batch_data(yaml_file: yaml_file, json_file: json_file)
|
190
|
+
data.each { |group_name, data|
|
170
191
|
|
171
|
-
|
172
|
-
if delete_params(group_name, data["delete_params"])
|
173
|
-
update_group(group_name, classes: data["delete_params"])
|
174
|
-
end
|
175
|
-
end
|
192
|
+
Escort::Logger.output.puts "Processing #{group_name}"
|
176
193
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
194
|
+
if data.has_key?("delete_classes")
|
195
|
+
if delete_classes(group_name, data["delete_classes"])
|
196
|
+
update_group(group_name, classes: data["delete_classes"])
|
197
|
+
end
|
198
|
+
end
|
182
199
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
end
|
188
|
-
}
|
189
|
-
rescue Psych::SyntaxError
|
190
|
-
Escort::Logger.error.error "Syntax error found in #{filename}, please fix and retry"
|
200
|
+
if data.has_key?("delete_params")
|
201
|
+
if delete_params(group_name, data["delete_params"])
|
202
|
+
update_group(group_name, classes: data["delete_params"])
|
203
|
+
end
|
191
204
|
end
|
192
|
-
|
193
|
-
|
194
|
-
|
205
|
+
|
206
|
+
if data.has_key?("classes")
|
207
|
+
if ensure_classes_and_params(group_name, data["classes"])
|
208
|
+
update_group(group_name, classes: data["classes"])
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
if data.has_key?("append_rules")
|
213
|
+
if ensure_rules(group_name, data["append_rules"])
|
214
|
+
update_group(group_name, rule: data["append_rules"])
|
215
|
+
end
|
216
|
+
end
|
217
|
+
}
|
195
218
|
end
|
196
219
|
|
197
220
|
def self.delete_class(group, class_name)
|
@@ -279,16 +302,26 @@ module NCEdit
|
|
279
302
|
updated
|
280
303
|
end
|
281
304
|
|
305
|
+
# Ensure a partualar rule exists in the group["rule"] array
|
306
|
+
# This affects only the items in the chain, eg:
|
307
|
+
# [
|
308
|
+
# "or",
|
309
|
+
# [
|
310
|
+
# <--- here!
|
311
|
+
# ]
|
312
|
+
# ]
|
313
|
+
#
|
314
|
+
# Only the rule to be added in should be passed as the rule parameter, eg:
|
315
|
+
# ["=", "name", "bob"]
|
282
316
|
def self.ensure_rule(group, rule)
|
283
317
|
updated = false
|
284
|
-
if ! group["rules"]
|
285
|
-
# no rules yet - just add our new one
|
286
|
-
group["rules"] = []
|
287
|
-
end
|
288
318
|
|
289
319
|
# see if rule already exists, if it doesn't, append it
|
290
320
|
found = false
|
291
|
-
|
321
|
+
|
322
|
+
# rules are nested like this, the "or" applies to the whole rule chain:
|
323
|
+
# "rule"=>["or", ["=", "name", "bob"], ["=", "name", "hello"]]
|
324
|
+
group["rule"][1].each {|system_rule|
|
292
325
|
if system_rule[0] == rule[0] and
|
293
326
|
system_rule[1] == rule[1] and
|
294
327
|
system_rule[2] == rule[2]
|
@@ -298,20 +331,45 @@ module NCEdit
|
|
298
331
|
}
|
299
332
|
if ! found
|
300
333
|
Escort::Logger.output.puts "Appending rule: #{rule}"
|
301
|
-
group["
|
334
|
+
group["rule"][1] << rule
|
302
335
|
updated = true
|
303
336
|
end
|
304
337
|
|
305
338
|
updated
|
306
339
|
end
|
307
340
|
|
341
|
+
# rules need to arrive like this:
|
342
|
+
# ["or", ["=", "name", "pupper.megacorp.com"], ["=", "name", "pupper.megacorp.com"]]
|
343
|
+
# since the rule conjunction "or" can only be specified once per rule chain
|
344
|
+
# we will replace whatever already exists in the rule with what the user
|
345
|
+
# specified
|
308
346
|
def self.ensure_rules(group_name, rules)
|
309
347
|
updated = false
|
310
|
-
|
311
|
-
|
348
|
+
group = nc_group(group_name)
|
349
|
+
if ! group["rule"] or group["rule"].empty?
|
350
|
+
# no rules yet - just add our new one
|
351
|
+
group["rule"] = [DEFAULT_RULE,[]]
|
352
|
+
end
|
353
|
+
updated |= ensure_rule_conjunction(group, rules[0])
|
354
|
+
rules[1].each { |rule|
|
355
|
+
updated |= ensure_rule(group, rule)
|
312
356
|
}
|
313
357
|
|
314
358
|
updated
|
315
359
|
end
|
360
|
+
|
361
|
+
def self.ensure_rule_conjunction(group, op)
|
362
|
+
updated = false
|
363
|
+
if ["and", "or"].include?(op)
|
364
|
+
if group["rule"][0] != op
|
365
|
+
group["rule"][0] = op
|
366
|
+
updated = true
|
367
|
+
end
|
368
|
+
else
|
369
|
+
raise "Illegal rule conjunction #{op}, allowed: 'and', 'or'"
|
370
|
+
end
|
371
|
+
|
372
|
+
updated
|
373
|
+
end
|
316
374
|
end
|
317
375
|
end
|
data/lib/ncedit/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ncedit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Geoff Williams
|
@@ -109,9 +109,10 @@ files:
|
|
109
109
|
- LICENSE
|
110
110
|
- README.md
|
111
111
|
- Rakefile
|
112
|
-
- batch.yaml
|
113
112
|
- bin/console
|
114
113
|
- bin/setup
|
114
|
+
- doc/example/batch.json
|
115
|
+
- doc/example/batch.yaml
|
115
116
|
- exe/ncedit
|
116
117
|
- lib/ncedit.rb
|
117
118
|
- lib/ncedit/cmd.rb
|