Skip to content

Instantly share code, notes, and snippets.

@paulmooring
Created August 5, 2016 17:31
Show Gist options
  • Save paulmooring/7d97e447e5b5f613715e4bcbf9a6bd8e to your computer and use it in GitHub Desktop.
Save paulmooring/7d97e447e5b5f613715e4bcbf9a6bd8e to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
require 'net/http'
require 'uri'
require 'json'
require 'optparse'
opts = {}
option_parser = OptionParser.new do |o|
o.banner = "Usage: #{$0} [-h] [-vq] -H HOST -t TOKEN -n NODE -p PROFILE -i IMPACT"
o.on('-H', '--host COMPLIANCE_SERVER', 'Hostname or IP of Compliance server') do |host|
opts[:host] = host.to_s
end
o.on('-t', '--token REFRESH_TOKEN', 'Compliance API refresh token') do |token|
opts[:token] = token.to_s
end
o.on('-n', '--node-name NODE_NAME', 'Node name to check') do |node|
opts[:node] = node.to_s
end
o.on('-p', '--profile PROFILE_OWNER/PROFILE_ID', 'Profile to check in the form of owner/id ("base/linux")') do |profile|
opts[:profile] = profile.to_s
end
o.on('-i', '--impact IMPACT_THRESHOLD', 'Threshold of impact to fail on (between 0.0 and 1.0)') do |impact|
opts[:impact] = impact.to_s
end
o.on_tail("-h", "--help", "Show this message") do
puts o
exit 3
end
end.parse!
opts[:host] ||= 'localhost'
opts[:profile] ||= 'base/linux'
opts[:impact] ||= 0.5
[:token, :node].each do |o|
unless opts.key? o
STDERR.puts "Missing required argument for #{o}!"
exit 3
end
end
class ComplianceHTTP
attr_accessor :host, :refresh_token, :access_token
def initialize(params = {})
@host = params.fetch(:host, 'localhost')
@refresh_token = params[:refresh_token]
@access_token = get_access_token
end
def get(path)
uri = URI.parse("https://#{@host}")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
req = Net::HTTP::Get.new(path)
req.add_field('Authorization', "Bearer #{@access_token}")
req.add_field('Content-Type', 'application/json')
resp = http.request(req).body
resp.empty? ? {} : JSON.parse(resp)
end
def post(path, data)
uri = URI.parse("https://#{@host}")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
req = Net::HTTP::Get.new(path)
req.add_field('Authorization', "Bearer #{@access_token}")
req.add_field('Content-Type', 'application/json')
req.body = data.to_json
JSON.parse http.request(req).body
end
private
def get_access_token
uri = URI.parse("https://#{@host}")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
req = Net::HTTP::Post.new('/api/login')
req.add_field('Content-Type', 'application/json')
req.body = "{\"token\": \"#{refresh_token}\"}"
JSON.parse(http.request(req).body)['access_token']
end
end
profile_owner, profile_id = opts[:profile].split('/')
impact_threshold ||= 0.0
com_http = ComplianceHTTP.new(:host => opts[:host], :refresh_token => opts[:token])
node_id = com_http.get('/api/owners/chefops/envs/_default/nodes').select do |node|
node['name'] == opts[:node]
end.first['id']
ctls = com_http.get("/api/owners/chefops/envs/_default/nodes/#{node_id}/compliance").select do |control|
control['profileOwner'] == profile_owner and control['profileID'] == profile_id
end
if ctls.empty?
STDERR.puts "No controls found for #{opts[:profile]}"
exit 2
end
fails = ctls.select do |ctl|
ctl['impact'] > opts[:impact] and ctl['failures'] != 0
end
if fails.length == 0
puts "No failures"
exit 0
else
puts "Failures on node #{opts[:node]}"
fails.each do |ctl|
STDERR.puts "Failed #{ctl['profileOwner']}/#{ctl['profileID']} - #{ctl['rule']}"
end
exit 1
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment