# encoding: utf-8require'utils/filter'require'hashie/mash'require'resources/package'moduleInspec::ResourcesclassElasticsearch<Inspec.resource(1)name'elasticsearch'supportsplatform: 'unix'desc"Use the Elasticsearch InSpec audit resource to test the status of nodes in
an Elasticsearch cluster."example<<~EXAMPLE
describe elasticsearch('http://eshost.mycompany.biz:9200/', username: 'elastic', password: 'changeme', ssl_verify: false) do
its('node_count') { should >= 3 }
end
describe elasticsearch do
its('node_name') { should include 'node1' }
its('os') { should_not include 'MacOS' }
its('version') { should cmp > 1.2.0 }
end
EXAMPLEfilter=FilterTable.createfilter.register_custom_matcher(:exists?){|x|!x.entries.empty?}filter.register_column(:cluster_name,field: 'cluster_name').register_column(:node_name,field: 'name').register_column(:transport_address,field: 'transport_address').register_column(:host,field: 'host').register_column(:ip,field: 'ip').register_column(:version,field: 'version').register_column(:build_hash,field: 'build_hash').register_column(:total_indexing_buffer,field: 'total_indexing_buffer').register_column(:roles,field: 'roles').register_column(:settings,field: 'settings').register_column(:os,field: 'os').register_column(:process,field: 'process').register_column(:jvm,field: 'jvm').register_column(:transport,field: 'transport').register_column(:http,field: 'http').register_column(:plugins,field: 'plugins').register_column(:plugin_list,field: 'plugin_list').register_column(:modules,field: 'modules').register_column(:module_list,field: 'module_list').register_column(:node_id,field: 'node_id').register_column(:ingest,field: 'ingest').register_custom_property(:node_count){|t,_|t.entries.length}filter.install_filter_methods_on_resource(self,:nodes)attr_reader:nodes,:urldefinitialize(opts={})returnskip_resource'Package `curl` not avaiable on the host'unlessinspec.command('curl').exist?@url=opts.fetch(:url,'http://localhost:9200')username=opts.fetch(:username,nil)password=opts.fetch(:password,nil)ssl_verify=opts.fetch(:ssl_verify,true)cmd=inspec.command(curl_command_string(username,password,ssl_verify))# after implementation of PR #2235, this begin..rescue won't be necessary.# The checks in verify_curl_success! can raise their own skip message exception.beginverify_curl_success!(cmd)rescue=>ereturnskip_resourcee.messageendbegincontent=JSON.parse(cmd.stdout)# after implementation of PR #2235, this can be broken out of the begin..rescue# clause. The checks in verify_json_payload! can raise their own skip message exception.verify_json_payload!(content)rescueJSON::ParserError=>ereturnskip_resource"Couldn't parse the Elasticsearch response: #{e.message}"rescue=>ereturnskip_resourcee.messageend@nodes=parse_cluster(content)enddefto_s"Elasticsearch Cluster #{url}"endprivatedefparse_cluster(content)return[]unlesscontent['nodes']nodes=[]content['nodes'].eachdo|node_id,node_data|node_data=fix_mash_key_collision(node_data)node=Hashie::Mash.new(node_data)node.node_id=node_idnode.plugin_list=node.plugins.map(&:name)node.module_list=node.modules.map(&:name)node.cluster_name=node.settings.cluster.namenodes<<nodeendnodesend## Hashie::Mash will throw warnings if the Mash contains a key that is the same as a built-in# method on a Hashie::Mash instance. This is a crude way of avoiding those warnings without# hard-coding a bunch of key renames.## Any key that is in conflict will be renamed "es_ORIGINALKEY"#deffix_mash_key_collision(data)test_mash=Hashie::Mash.newnew_data={}data.eachdo|key,value|new_key=test_mash.respond_to?(key.to_sym)?"es_#{key}":keynew_value=value.is_a?(Hash)?fix_mash_key_collision(value):valuenew_data[new_key]=new_valueendnew_dataenddefcurl_command_string(username,password,ssl_verify)cmd_string=['curl']cmd_string<<'-k'unlessssl_verifycmd_string<<"-H 'Content-Type: application/json'"cmd_string<<" -u #{username}:#{password}"unlessusername.nil?||password.nil?cmd_string<<URI.join(url,'_nodes')cmd_string.join(' ')enddefverify_curl_success!(cmd)# the following lines captures known possible curl command errors and provides compact skip resource messegesifcmd.stderr=~/Failed to connect/raise"Connection refused - please check the URL #{url} for accuracy"endifcmd.stderr=~/Peer's Certificate issuer is not recognized/raise'Connection refused - peer certificate issuer is not recognized'endraise"Error fetching Elastcsearch data from curl #{url}: #{cmd.stderr}"unlesscmd.exit_status.zero?enddefverify_json_payload!(content)unlesscontent['error'].nil?raise"#{content['error']['type']}: #{content['error']['reason']}"endraise'No successful nodes available in cluster'ifcontent['_nodes']['successful'].zero?endendend