Skip to content

Commit afeb230

Browse files
authored
Merge pull request #21 from WhatsARanjit/face_base
Initial commit on face
2 parents 4bab5c3 + 2f99ce9 commit afeb230

File tree

5 files changed

+348
-0
lines changed

5 files changed

+348
-0
lines changed

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
* [Puppet_environment](#puppet_environment)
1111
1. [Functions](#functions)
1212
* [node_groups()](#node_groups)
13+
1. [Face](#face)
1314
1. [Things to do](#things-to-do)
1415

1516
## Overview
@@ -197,6 +198,34 @@ Retrieve all or one node_group and its data.
197198

198199
_Type:_ rvalue
199200

201+
## Face
202+
203+
The `node_manager` face allows you to interact with endpoints other than
204+
the groups endpoint using the type or function. Use the `--help` flag
205+
to explore functionaliy of each action.
206+
207+
```
208+
# puppet node_manager --help
209+
210+
USAGE: puppet node_manager <action>
211+
212+
Interact with node classifier API
213+
214+
OPTIONS:
215+
--render-as FORMAT - The rendering format to use.
216+
--verbose - Whether to log verbosely.
217+
--debug - Whether to log debug information.
218+
219+
ACTIONS:
220+
classes List class information
221+
classified List classification information
222+
environments Query environment sync status
223+
groups List group information
224+
unpin Unpin a node from all groups
225+
226+
See 'puppet man node_manager' or 'man puppet-node_manager' for full help.
227+
```
228+
200229
## Things to do
201230

202231
* Remove `puppetclassify` dependency

docs/face.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
## `node_manager` face
2+
3+
```
4+
USAGE: puppet node_manager <action>
5+
6+
Interact with node classifier API
7+
8+
OPTIONS:
9+
--render-as FORMAT - The rendering format to use.
10+
--verbose - Whether to log verbosely.
11+
--debug - Whether to log debug information.
12+
13+
ACTIONS:
14+
classes List class information
15+
classified List classification information
16+
environments Query environment sync status
17+
groups List group information
18+
unpin Unpin a node from all groups
19+
```
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
require 'puppet/face'
2+
require 'puppet/application/face_base'
3+
4+
class Puppet::Application::Node_manager < Puppet::Application::FaceBase
5+
end

lib/puppet/face/node_manager.rb

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
require 'puppet'
2+
require 'puppet/face'
3+
require 'puppet/util/nc_https'
4+
require 'puppet_x/node_manager/common'
5+
6+
Puppet::Face.define(:node_manager, '0.1.0') do
7+
summary 'Interact with node classifier API'
8+
copyright 'WhatsARanjit', 2017
9+
license 'Apache-2.0'
10+
11+
classifier = Puppet::Util::Nc_https.new
12+
output = []
13+
14+
action :groups do
15+
summary 'List group information'
16+
arguments '[group_name]'
17+
18+
option '--export' do
19+
summary 'Provide formatted JSON for import'
20+
default_to { false }
21+
end
22+
23+
option '--import JSONFILE' do
24+
summary 'Import formatted JSON of groups'
25+
default_to { false }
26+
end
27+
28+
when_invoked do |*args|
29+
options = args.last
30+
31+
if options[:import]
32+
fail('Choose import or export') if options[:export]
33+
34+
'Success' if classifier.import_hierarchy(read_import(options[:import]))
35+
else
36+
groups = classifier.get_groups
37+
38+
if args.length == 2
39+
output << groups.select { |g| g['name'] == args.first }
40+
elsif args.length == 1
41+
output << groups
42+
else
43+
fail("wrong number of arguments (#{args.length-1} for 1)")
44+
end
45+
if options[:export]
46+
output.flatten.to_json
47+
else
48+
PuppetX::Node_manager::Common.hashify_group_array(output.flatten)
49+
end
50+
end
51+
end
52+
53+
when_rendering :console do |output|
54+
case output
55+
when Hash
56+
if output.length == 0
57+
fail('No groups found')
58+
elsif output.length == 1
59+
JSON.pretty_generate output.values[0]
60+
else
61+
output.keys
62+
end
63+
else
64+
output
65+
end
66+
end
67+
end
68+
69+
action :classes do
70+
summary 'List class information'
71+
arguments '[class]'
72+
73+
option '--update' do
74+
summary 'Trigger classifier to update class list'
75+
default_to { false }
76+
end
77+
78+
when_invoked do |*args|
79+
options = args.last
80+
klass = args.first.is_a?(String) ? args.first : false
81+
env = options[:environment] ? options[:environment] : 'production'
82+
queryenv = options[:environment] ? options[:environment] : nil
83+
84+
if options[:update]
85+
'Success' if classifier.update_classes(queryenv)
86+
else
87+
output << classifier.get_classes(env, klass)
88+
PuppetX::Node_manager::Common.hashify_group_array(output.flatten)
89+
end
90+
end
91+
92+
when_rendering :console do |output|
93+
case output
94+
when Hash
95+
if output.length <= 1
96+
JSON.pretty_generate output.values[0]
97+
else
98+
output.keys
99+
end
100+
else
101+
output
102+
end
103+
end
104+
end
105+
106+
action :classified do
107+
summary 'List classification information'
108+
arguments 'nodename'
109+
110+
option '--explain' do
111+
summary 'Provide explanation'
112+
default_to { false }
113+
end
114+
115+
option '--facts FACTSFILE' do
116+
summary 'Provide facts YAML or JSON'
117+
default_to { false }
118+
end
119+
120+
option '--trusted TRUSTEDFILE' do
121+
summary 'Provide trusted facts YAML or JSON'
122+
default_to { false }
123+
end
124+
125+
when_invoked do |nodename, options|
126+
output << classifier.get_classified(
127+
nodename,
128+
options[:explain],
129+
check_facts(options[:facts], options),
130+
check_facts(options[:trusted], options),
131+
)
132+
output.flatten
133+
end
134+
135+
when_rendering :console do |output, nodename, options|
136+
if options[:explain] == true
137+
JSON.pretty_generate output.first['match_explanations']
138+
else
139+
JSON.pretty_generate output.first['classes']
140+
end
141+
end
142+
end
143+
144+
action :unpin do
145+
summary 'Unpin a node from all groups'
146+
arguments 'nodename'
147+
148+
when_invoked do |nodename, options|
149+
output << classifier.unpin_from_all(nodename)
150+
if output.flatten.first['nodes'].empty?
151+
'Found nothing to unpin.'
152+
else
153+
PuppetX::Node_manager::Common.hashify_group_array(output.flatten['nodes'].first)
154+
end
155+
end
156+
157+
when_rendering :console do |output, nodename, options|
158+
if output.is_a?(String)
159+
output
160+
else
161+
JSON.pretty_generate output
162+
end
163+
end
164+
end
165+
166+
action :environments do
167+
summary 'Query environment sync status'
168+
arguments '[environment]'
169+
170+
when_invoked do |*args|
171+
options = args.last
172+
environments = classifier.get_environments
173+
174+
if args.length == 2
175+
output << environments.select { |g| g['name'] == args.first }
176+
elsif args.length == 1
177+
output << environments
178+
else
179+
fail("wrong number of arguments (#{args.length-1} for 1)")
180+
end
181+
output.flatten
182+
end
183+
184+
when_rendering :console do |output|
185+
if output.length > 1
186+
output
187+
else
188+
if output.first.class == Hash && output.first.has_key?('sync_succeeded')
189+
output.first['sync_succeeded'].to_s
190+
else
191+
fail('Environment doesn\'t exist.')
192+
end
193+
end
194+
end
195+
end
196+
197+
def check_facts(file, options)
198+
if file && options[:explain]
199+
begin
200+
contents = YAML.load_file(file)
201+
content ||= JSON.parse(File.read file)
202+
rescue
203+
fail "Could not file file '#{file}'"
204+
else
205+
contents
206+
end
207+
else
208+
{}
209+
end
210+
end
211+
212+
def read_import(file)
213+
begin
214+
contents = JSON.parse(File.read file)
215+
rescue
216+
fail "Could not read file '#{file}'"
217+
else
218+
contents
219+
end
220+
end
221+
222+
def cap_class_name(k)
223+
k.split('::').collect { |p| p.capitalize }.join('::')
224+
end
225+
end

lib/puppet/util/nc_https.rb

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,76 @@ def update_group(data)
8080
end
8181
end
8282

83+
def import_hierarchy(data)
84+
res = do_https('v1/import-hierarchy', 'POST', data)
85+
if res.code.to_i != 204
86+
Puppet.debug("Response code: #{res.code}")
87+
Puppet.debug("Response message: #{res.body}")
88+
fail('Unable to import node_groups')
89+
else
90+
true
91+
end
92+
end
93+
94+
def get_classes(env = false, name = false)
95+
url_array = ['v1']
96+
url_array << 'classes' unless env
97+
url_array << "environments/#{env}/classes" if env
98+
url_array << name if name
99+
res = do_https(url_array.join('/'), 'GET')
100+
if res.code.to_i != 200
101+
Puppet.debug("Response code: #{res.code}")
102+
Puppet.debug("Response message: #{res.body}")
103+
fail JSON.parse(res.body)['msg']
104+
else
105+
JSON.parse(res.body)
106+
end
107+
end
108+
109+
def update_classes(env = nil)
110+
url_array = ['v1/update-classes']
111+
url_array << "?environment=#{env}" if env
112+
res = do_https(url_array.join('/'), 'POST')
113+
if res.code.to_i != 201
114+
Puppet.debug("Response code: #{res.code}")
115+
Puppet.debug("Response message: #{res.body}")
116+
fail('Unable to update classes')
117+
else
118+
true
119+
end
120+
end
121+
122+
def get_classified(name, expl = false, facts = {}, trusted = {})
123+
url_array = ['v1/classified/nodes']
124+
url_array << name
125+
url_array << 'explanation' if expl
126+
data = facts.merge(trusted)
127+
res = do_https(url_array.join('/'), 'POST', data)
128+
require 'pry'; binding.pry
129+
if res.code.to_i != 200
130+
Puppet.debug("Response code: #{res.code}")
131+
Puppet.debug("Response message: #{res.body}")
132+
else
133+
JSON.parse(res.body)
134+
end
135+
end
136+
137+
def unpin_from_all(node)
138+
data = { 'nodes' => [node] }
139+
res = do_https('v1/commands/unpin-from-all', 'POST', data)
140+
if res.code.to_i != 200
141+
Puppet.debug("Response code: #{res.code}")
142+
Puppet.debug("Response message: #{res.body}")
143+
else
144+
JSON.parse(res.body)
145+
end
146+
end
147+
148+
def get_environments
149+
res = do_https('v1/environments', 'GET')
150+
JSON.parse(res.body)
151+
end
152+
83153
private
84154

85155
def do_https(endpoint, method = 'post', data = {})

0 commit comments

Comments
 (0)