ncedit 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 509d160cbfff794ee5d1880dbde293aa6fedb7dd
4
+ data.tar.gz: cccf9de5724dc7e43397625985d2cdf9a9eb625e
5
+ SHA512:
6
+ metadata.gz: b195209858825b5924aa7e975525760622caa1ba60a1fea8f12c6a4ac7f28318ea2e82f29849276433d7ff465cae8fc1719cdf88a03a29a6f934f6acad537a60
7
+ data.tar.gz: bbc67d80b0b6d7c6ed5f00dc646dd03cfc06ce0bff54db67fabc48025fd3d09b26f6e5d9c3d6f7072f4db921ad7dec6a4ba5d243877f06fc15d6a61ec24c09cc
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.2.3
5
+ before_install: gem install bundler -v 1.12.5
6
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ncedit.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "{}"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright {yyyy} {name of copyright owner}
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,36 @@
1
+ [![Build Status](https://travis-ci.org/GeoffWilliams/ncedit.svg?branch=master)](https://travis-ci.org/GeoffWilliams/ncedit)
2
+ # Ncedit
3
+
4
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/ncedit`. To experiment with that code, run `bin/console` for an interactive prompt.
5
+
6
+ TODO: Delete this and the text above, and describe your gem
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'ncedit'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install ncedit
23
+
24
+ ## Usage
25
+
26
+ TODO: Write usage instructions here
27
+
28
+ ## Development
29
+
30
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
31
+
32
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
33
+
34
+ ## Contributing
35
+
36
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/ncedit.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/batch.yaml ADDED
@@ -0,0 +1,26 @@
1
+ # Example YAML file containing batch updates for ncedit
2
+ # To process these changes, on the puppet master do:
3
+ # ncedit batch --filename /PATH/TO/THIS/FILE
4
+ #
5
+ # ncedit is idempotent so run the command as often as you like
6
+ "PE Master":
7
+ "classes":
8
+ "puppet_enterprise::profile::master":
9
+ "code_manager_auto_configure": true
10
+ "r10k_remote": "https://github.com/GeoffWilliams/r10k-control"
11
+ "r10k_private_key": "/etc/puppetlabs/puppetserver/ssh/id-control_repo.rsa"
12
+ #
13
+ # "delete_classes":
14
+ # - "puppet_enterprise::profile::masterbad"
15
+ #
16
+ # "delete_params":
17
+ # "puppet_enterprise::profile::redo:":
18
+ # - "badparam"
19
+
20
+ "Puppet Masters":
21
+ "classes":
22
+ "r_role::puppet::master_minimal": {}
23
+ # "append_rules":
24
+ # - - "="
25
+ # - "name"
26
+ # - "vmpump02.puppet.com"
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "ncedit"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/ncedit ADDED
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env ruby
2
+ require 'escort'
3
+ require 'ncedit/version'
4
+ require 'ncedit/cmd'
5
+
6
+ # display help if nothing specified
7
+ ARGV.push('-h') if ARGV.empty?
8
+
9
+ Escort::App.create do |app|
10
+ app.version NCEdit::VERSION
11
+ app.summary "ncedit"
12
+ app.description "Edit PE node classification groups"
13
+
14
+ # not currently working...
15
+ # app.command :classes do |command|
16
+ # command.summary "Install all known nodes"
17
+ # command.description "Install Puppet Enterprise master and agents on all known nodes"
18
+ # command.options do |opts|
19
+ # opts.opt(:group_name,
20
+ # 'NC group name',
21
+ # :long => '--group-name',
22
+ # :type => :string,
23
+ # )
24
+ # opts.opt(:class_name,
25
+ # 'NC class name',
26
+ # :long => '--class-name',
27
+ # :type => :string,
28
+ # )
29
+ # opts.opt(:param_name,
30
+ # 'NC parameter name',
31
+ # :long => '--param-name',
32
+ # :type => :string,
33
+ # )
34
+ # opts.opt(:param_value,
35
+ # 'NC parameter value',
36
+ # :long => '--param-value',
37
+ # :type => :string,
38
+ # )
39
+ # opts.opt(:delete_class,
40
+ # 'delete this rule',
41
+ # :long => '--delete-class',
42
+ # :type => :boolean,
43
+ # :default => false,
44
+ # )
45
+ # opts.opt(:delete_param,
46
+ # 'delete this rule',
47
+ # :long => '--delete-param',
48
+ # :type => :boolean,
49
+ # :default => false,
50
+ # )
51
+ # opts.dependency :param_value, :on => :param_name
52
+ # end
53
+ # command.action do |options, arguments|
54
+ # NCEdit::Cmd::classes(options, arguments)
55
+ # end
56
+ # end
57
+
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"
61
+ command.options do |opts|
62
+ opts.opt(:filename,
63
+ 'YAML file',
64
+ :long => '--filename',
65
+ :type => :string,
66
+ )
67
+ end
68
+ command.action do |options, arguments|
69
+ filename = options[:global][:commands][:batch][:options][:filename]
70
+ if filename == nil
71
+ Escort::Logger.error.error "Filename is required for batch updates"
72
+ else
73
+ NCEdit::Cmd::batch(filename)
74
+ end
75
+ end
76
+ end
77
+ end
data/lib/ncedit/cmd.rb ADDED
@@ -0,0 +1,317 @@
1
+ require 'puppetclassify'
2
+ require 'yaml'
3
+ require 'escort'
4
+
5
+ module NCEdit
6
+ module Cmd
7
+
8
+ def self.init(puppetclassify = nil)
9
+ if puppetclassify
10
+ # use passed in puppetclassify if present - allows injection for easy
11
+ # tesing - otherwise make a real one
12
+ @puppetclassify = puppetclassify
13
+ else
14
+ hostname = %x(facter fqdn).strip.downcase
15
+ port = 4433
16
+
17
+ # Define the url to the classifier API - we can't just do localhost because
18
+ # the name has to match the SSL certificate
19
+ rest_api_url = "https://#{hostname}:#{port}/classifier-api"
20
+
21
+ # We need to authenticate against the REST API using a certificate
22
+ # that is whitelisted in /etc/puppetlabs/console-services/rbac-certificate-whitelist.
23
+ # (https://docs.puppetlabs.com/pe/latest/nc_forming_requests.html#authentication)
24
+ #
25
+ # Since we're doing this on the master,
26
+ # we can just use the internal dashboard certs for authentication
27
+ ssl_dir = '/etc/puppetlabs/puppet/ssl'
28
+ ca_cert = "#{ssl_dir}/ca/ca_crt.pem"
29
+ cert_name = hostname.downcase
30
+ cert = "#{ssl_dir}/certs/#{cert_name}.pem"
31
+ private_key = "#{ssl_dir}/private_keys/#{cert_name}.pem"
32
+
33
+ auth_info = {
34
+ 'ca_certificate_path' => ca_cert,
35
+ 'certificate_path' => cert,
36
+ 'private_key_path' => private_key,
37
+ }
38
+
39
+ # wait upto 5 mins for classifier to become live...
40
+ port_open = false
41
+ Timeout::timeout(300) do
42
+ while not port_open
43
+ begin
44
+ s = TCPSocket.new(hostname, port)
45
+ s.close
46
+ port_open = true
47
+ Escort::Logger.output.puts "Classifier signs of life detected, proceeding to classify..."
48
+ rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
49
+ Escort::Logger.output.puts "connection refused, waiting..."
50
+ sleep(1)
51
+ end
52
+ end
53
+ end
54
+
55
+ @puppetclassify = PuppetClassify.new(rest_api_url, auth_info)
56
+ end
57
+ end
58
+
59
+ # Fetch a group by ID, make the group if it doesn't already exist
60
+ def self.nc_group_id(group_name)
61
+ if ! @puppetclassify
62
+ init
63
+ end
64
+
65
+ group_id = @puppetclassify.groups.get_group_id(group_name)
66
+ if group_id == nil
67
+ Escort::Logger.output.puts "Group: #{group_name} does not exist, creating..."
68
+ res = @puppetclassify.groups.create_group(
69
+ {
70
+ "name" => group_name,
71
+ "parent" => @puppetclassify.groups.get_group_id("All Nodes"),
72
+ "classes" => {},
73
+ }
74
+ )
75
+ if res == nil
76
+ raise "Error creating group #{group_name}"
77
+ end
78
+
79
+ # re-fetch the group id
80
+ group_id = @puppetclassify.groups.get_group_id(group_name)
81
+ end
82
+
83
+ group_id
84
+ end
85
+
86
+ def self.nc_group(group_name)
87
+ if ! @puppetclassify
88
+ init
89
+ end
90
+ # Get the wanted group from the API
91
+ # 1. Get the id of the wanted group
92
+ # 2. Use the id to fetch the group
93
+ group_id = nc_group_id(group_name)
94
+ Escort::Logger.output.puts "Group #{group_name} found, getting definition"
95
+ group = @puppetclassify.groups.get_group(group_id)
96
+
97
+ group
98
+ end
99
+
100
+ def self.update_group(group_name, classes: nil, rule: nil)
101
+ # group_delta will actually replace all classes/rules with whatever is
102
+ # specified, so we need to merge this with any existing definition if
103
+ # one of these fields is not needed for a particular update otherwise
104
+ # updating just the classes would remove the current rule!
105
+ if classes == nil
106
+ classes = nc_group(group_name)["classes"]
107
+ end
108
+
109
+ if rule == nil
110
+ rule = nc_group(group_name)["rule"]
111
+ end
112
+
113
+ group_delta = {
114
+ 'id' => nc_group_id(group_name),
115
+ 'rule' => rule,
116
+ 'classes' => classes,
117
+ }
118
+ res = @puppetclassify.groups.update_group(group_delta)
119
+
120
+ # due to the way the puppetclassify gem is written, we get a nil response
121
+ # on every request, whether it passed or failed. Therefore, to test that
122
+ # our update was processed correctly, the only thing we can do is to fetch
123
+ # the group again from puppetclassify and check that all of our values are
124
+ # now present. If there was an error, then the user should have
125
+ # previously seen some output since puppetclassify prints some useful
126
+ # debug output
127
+ saved_group = nc_group(group_name)
128
+ if saved_group["classes"] == classes and saved_group["rule"] == rule
129
+ Escort::Logger.output.puts "changes saved"
130
+ else
131
+ Escort::Logger.error.error "re-read #{group_name} results in #{saved_group} should have delta of #{group_delta}"
132
+ raise "Error saving #{group_name}"
133
+ end
134
+ end
135
+
136
+ # Batch entry from YAML file, example file format:
137
+ # 'PE Master':
138
+ # 'classes':
139
+ # 'puppet_enterprise::profile::master':
140
+ # 'r10k_remote': 'http://blah'
141
+ # 'r10k_private_key': '/etc/topsecret'
142
+ #
143
+ # 'delete_classes':
144
+ # 'puppet_enterprise::profile::masterbad'
145
+ #
146
+ # 'delete_params':
147
+ # 'puppet_enterprise::profile::redo:
148
+ # 'badparam'
149
+ #
150
+ # 'Puppet Masters':
151
+ # 'clases':
152
+ # 'role::puppet::master':
153
+ # 'append_rules':
154
+ # - - "="
155
+ # - "name"
156
+ # - "vmpump02.puppet.com"
157
+ def self.batch(filename)
158
+ if File.exists?(filename)
159
+ begin
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
170
+
171
+ if data.has_key?("delete_params")
172
+ if delete_params(group_name, data["delete_params"])
173
+ update_group(group_name, classes: data["delete_params"])
174
+ end
175
+ end
176
+
177
+ if data.has_key?("classes")
178
+ if ensure_classes_and_params(group_name, data["classes"])
179
+ update_group(group_name, classes: data["classes"])
180
+ end
181
+ end
182
+
183
+ if data.has_key?("append_rules")
184
+ if ensure_rules(group_name, data["append_rules"])
185
+ update_group(group_name, rule: data["append_rules"])
186
+ end
187
+ end
188
+ }
189
+ rescue Psych::SyntaxError
190
+ Escort::Logger.error.error "Syntax error found in #{filename}, please fix and retry"
191
+ end
192
+ else
193
+ Escort::Logger.error.error "File not found: #{filename}"
194
+ end
195
+ end
196
+
197
+ def self.delete_class(group, class_name)
198
+ if group["classes"].delete(class_name)
199
+ changes = true
200
+ else
201
+ changes = false
202
+ end
203
+
204
+ changes
205
+ end
206
+
207
+ def self.delete_param(group, class_name, param_name)
208
+ if group["classes"].has_key?(class_name) and
209
+ group["classes"][class_name].delete(param_name)
210
+ changes = true
211
+ else
212
+ changes = false
213
+ end
214
+ changes
215
+ end
216
+
217
+ def self.ensure_class(group, class_name)
218
+ if ! group["classes"].has_key?(class_name)
219
+ group["classes"][class_name] = {}
220
+ changes = true
221
+ else
222
+ changes = false
223
+ end
224
+
225
+ changes
226
+ end
227
+
228
+ def self.ensure_param(group, class_name, param_name, param_value)
229
+ # ensure parameter set if specified
230
+ if ! group["classes"][class_name].has_key?(param_name) or
231
+ group["classes"][class_name][param_name] != param_value
232
+ group["classes"][class_name][param_name] = param_value
233
+ changes = true
234
+ else
235
+ changes = false
236
+ end
237
+
238
+ changes
239
+ end
240
+
241
+ def self.delete_classes(group_name, data)
242
+ updated = false
243
+ if data
244
+ data.each{ |class_name|
245
+ Escort::Logger.output.puts "Deleting class: #{group_name}->#{class_name}"
246
+ updated |= delete_class(nc_group(group_name), class_name)
247
+ }
248
+ end
249
+ updated
250
+ end
251
+
252
+ def self.delete_params(group_name, data)
253
+ updated = false
254
+ if data
255
+ data.each{ |class_name, param_names|
256
+ param_names.each { |param_name|
257
+ Escort::Logger.output.puts "Deleting param: #{group_name}->#{class_name}=>#{param_name}"
258
+ updated |= delete_param(nc_group(group_name), class_name, param_name)
259
+ }
260
+ }
261
+ end
262
+ updated
263
+ end
264
+
265
+ def self.ensure_classes_and_params(group_name, data)
266
+ updated = false
267
+ if data
268
+ data.each{ |class_name, params|
269
+ Escort::Logger.output.puts "ensuring class: #{group_name}->#{class_name}"
270
+ updated |= ensure_class(nc_group(group_name), class_name)
271
+ if params
272
+ params.each { |param_name, param_value|
273
+ Escort::Logger.output.puts "ensuring param: #{group_name}->#{class_name}->#{param_name}=#{param_value}"
274
+ updated |= ensure_param(nc_group(group_name), class_name, param_name, param_value)
275
+ }
276
+ end
277
+ }
278
+ end
279
+ updated
280
+ end
281
+
282
+ def self.ensure_rule(group, rule)
283
+ updated = false
284
+ if ! group["rules"]
285
+ # no rules yet - just add our new one
286
+ group["rules"] = []
287
+ end
288
+
289
+ # see if rule already exists, if it doesn't, append it
290
+ found = false
291
+ group["rules"].each {|system_rule|
292
+ if system_rule[0] == rule[0] and
293
+ system_rule[1] == rule[1] and
294
+ system_rule[2] == rule[2]
295
+ # rule found
296
+ found = true
297
+ end
298
+ }
299
+ if ! found
300
+ Escort::Logger.output.puts "Appending rule: #{rule}"
301
+ group["rules"] << rule
302
+ updated = true
303
+ end
304
+
305
+ updated
306
+ end
307
+
308
+ def self.ensure_rules(group_name, rules)
309
+ updated = false
310
+ rules.each { |rule|
311
+ updated |= ensure_rule(nc_group(group_name), rule)
312
+ }
313
+
314
+ updated
315
+ end
316
+ end
317
+ end
@@ -0,0 +1,3 @@
1
+ module NCEdit
2
+ VERSION = "0.0.1"
3
+ end
data/lib/ncedit.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "ncedit/version"
2
+
3
+ module NCEdit
4
+ # Your code goes here...
5
+ end
data/ncedit.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ncedit/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ncedit"
8
+ spec.version = NCEdit::VERSION
9
+ spec.authors = ["Geoff Williams"]
10
+ spec.email = ["geoff@geoffwilliams.me.uk"]
11
+
12
+ spec.summary = %q{Edit Puppet Enterprise Node Classifier rules}
13
+ spec.description = %q{Use the puppet-classify gem to create/edit NC rules}
14
+ spec.homepage = "https://github.com/GeoffWilliams/ncedit"
15
+
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.14"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rspec", "~> 3.0"
27
+
28
+ spec.add_runtime_dependency "escort", "0.4.0"
29
+ spec.add_runtime_dependency "json", "2.0.3"
30
+ spec.add_runtime_dependency "puppetclassify", "0.1.5"
31
+ end
metadata ADDED
@@ -0,0 +1,143 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ncedit
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Geoff Williams
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-03-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.14'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.14'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: escort
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 0.4.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 0.4.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: json
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '='
74
+ - !ruby/object:Gem::Version
75
+ version: 2.0.3
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '='
81
+ - !ruby/object:Gem::Version
82
+ version: 2.0.3
83
+ - !ruby/object:Gem::Dependency
84
+ name: puppetclassify
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '='
88
+ - !ruby/object:Gem::Version
89
+ version: 0.1.5
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '='
95
+ - !ruby/object:Gem::Version
96
+ version: 0.1.5
97
+ description: Use the puppet-classify gem to create/edit NC rules
98
+ email:
99
+ - geoff@geoffwilliams.me.uk
100
+ executables:
101
+ - ncedit
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - ".rspec"
107
+ - ".travis.yml"
108
+ - Gemfile
109
+ - LICENSE
110
+ - README.md
111
+ - Rakefile
112
+ - batch.yaml
113
+ - bin/console
114
+ - bin/setup
115
+ - exe/ncedit
116
+ - lib/ncedit.rb
117
+ - lib/ncedit/cmd.rb
118
+ - lib/ncedit/version.rb
119
+ - ncedit.gemspec
120
+ homepage: https://github.com/GeoffWilliams/ncedit
121
+ licenses: []
122
+ metadata: {}
123
+ post_install_message:
124
+ rdoc_options: []
125
+ require_paths:
126
+ - lib
127
+ required_ruby_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ required_rubygems_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ requirements: []
138
+ rubyforge_project:
139
+ rubygems_version: 2.5.2
140
+ signing_key:
141
+ specification_version: 4
142
+ summary: Edit Puppet Enterprise Node Classifier rules
143
+ test_files: []