Skip to content

Instantly share code, notes, and snippets.

@aaronkaka
Last active March 8, 2020 23:25
Show Gist options
  • Save aaronkaka/8491321 to your computer and use it in GitHub Desktop.
Save aaronkaka/8491321 to your computer and use it in GitHub Desktop.
Sonar Secured Widget for Dashing Framework

Preview

Screenshot

Description

Dashing widget to display Sonar metrics, connecting to either a secured or unsecured Sonar repository.

I created this widget after originally attempting to use EHadoux's simple Sonar widget, which did not allow access to my company's secured Sonar instance. Additionally, I implemented the ability to set up the widget using an external configuration file.

Please note that this is technically a job versus a widget, in that it periodically requests data from Sonar and pushes that to the list widget as the view.

Dependencies

The list widget packaged with the Dashing framework.

##Usage

Copy sonar.rb and sonar.cfg into the jobs directory.

Add the following snippet to the dashboard layout file:

<li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
 <div data-id="sonar" data-view="List" data-title="sonar stats" data-unordered="true" data-moreinfo="values as %">
 </div>
</li>

To display more metrics, increase the data-sizey attribute.

Configuration

Change the values for the configuration in sonar.cfg. All values are required except for username and password. If no username is provided, it is assumed the Sonar instance is unsecured. Order of display in the widget is dictated by the order of configured metrics.

If sonar.cfg is not found, the dashboard will fail to start with a "file not found" message.

Job implementation requires restarting Dashing if you change the configuration file.

server = "https://something.com/sonar"
key = "key"
widget_id = "sonar"
interval = "3600s"
metrics = "coverage,test_success_density"
username = "username"
password = "password"
require 'net/http'
require 'json'
require 'openssl'
require 'uri'
config_path = File.expand_path(File.join(File.dirname(__FILE__), "sonar.cfg"))
puts "Sonar widget configuration: " + config_path
configuration = Hash[File.read(config_path).scan(/(\S+)\s*=\s*"([^"]+)/)]
# Required config
server = "#{configuration['server']}".strip
key = "#{configuration['key']}".strip
id = "#{configuration['widget_id']}".strip
interval = "#{configuration['interval']}".strip
metrics = "#{configuration['metrics']}".strip
# Optional config for secured instances
username = "#{configuration['username']}".strip
password = "#{configuration['password']}"
if id.empty?
abort("MISSING widget id configuration!")
end
if interval.empty?
abort("MISSING interval configuration!")
end
if server.empty?
abort("MISSING server configuration!")
end
if key.empty?
abort("MISSING key configuration!")
end
if metrics.empty?
abort("MISSING metrics configuration!")
end
def get_val(json, key)
returnval = ''
json['msr'].find do |item|
returnval = item['val']
key == item['key']
end
returnval
end
SCHEDULER.every interval, :first_in => 0 do |job|
res = nil
uri = URI("#{server}/api/resources?resource=#{key}&metrics=#{metrics}")
# if no username is provided, assume sonar server is unsecured
if username.empty?
res = Net::HTTP.get(uri)
else
Net::HTTP.start(uri.host, uri.port,
:use_ssl => uri.scheme == 'https', :verify_mode => OpenSSL::SSL::VERIFY_NONE) do |http|
request = Net::HTTP::Get.new uri.request_uri
request.basic_auth username, password
res = http.request request
end
end
# Order of display is dictated by order of metrics in sonar.cfg
metricsArray = metrics.split(',')
hashArray = []
metricsArray.each { |x| hashArray.push({:label => x.gsub("_", " "), :value => get_val(JSON[res.body][0], x) }) }
send_event(id, { items: hashArray })
end
@christianhau
Copy link

Hi! I have installed the files and added the div to the dashboard, but I am getting this error, anything you have experienced before?

scheduler caught exception:
A JSON text must at least contain two octets!
/usr/lib/ruby/2.3.0/json/common.rb:156:in `initialize'
/usr/lib/ruby/2.3.0/json/common.rb:156:in `new'
/usr/lib/ruby/2.3.0/json/common.rb:156:in `parse'
/usr/lib/ruby/2.3.0/json/common.rb:15:in `[]'
/home/christian/smashing-dashboards/elhubdashboard/jobs/sonar.rb:69:in `block (2                                                                            levels) in <top (required)>'
/home/christian/smashing-dashboards/elhubdashboard/jobs/sonar.rb:69:in `each'
/home/christian/smashing-dashboards/elhubdashboard/jobs/sonar.rb:69:in `block in                                                                            <top (required)>'
/var/lib/gems/2.3.0/gems/rufus-scheduler-2.0.24/lib/rufus/sc/jobs.rb:230:in `tri                                                                           gger_block'
/var/lib/gems/2.3.0/gems/rufus-scheduler-2.0.24/lib/rufus/sc/jobs.rb:204:in `blo                                                                           ck in trigger'
/var/lib/gems/2.3.0/gems/rufus-scheduler-2.0.24/lib/rufus/sc/scheduler.rb:430:in                                                                            `block in trigger_job'

@SR-G
Copy link

SR-G commented Mar 8, 2020

Not sure if someone is still using this, i was willing to give a try to see what can be done with Smashing, but :

  • the SonarQube API has changed in the meanwhile (for example, /api/resources/ is now deprecated + JSON output has changed)
  • debugging the whole thing is not really easy (Smashing is not helping a lot and URI, JSON results ... should really be dumped / debugged out-of-the-box, IMHO)

Anyway the working GIST should now be (tested today on SonarQube 7.9.2) :

require 'net/http'
require 'json'
require 'openssl'
require 'uri'

config_path = File.expand_path(File.join(File.dirname(__FILE__), "sonar.cfg"))
puts "Sonar widget configuration: " + config_path
configuration = Hash[File.read(config_path).scan(/(\S+)\s*=\s*"([^"]+)/)]

# Required config
server = "#{configuration['server']}".strip
key = "#{configuration['key']}".strip
id = "#{configuration['widget_id']}".strip
interval = "#{configuration['interval']}".strip
metrics = "#{configuration['metrics']}".strip

# Optional config for secured instances
username = "#{configuration['username']}".strip
password = "#{configuration['password']}"

if id.empty?
    abort("MISSING widget id configuration!")
end
if interval.empty?
    abort("MISSING interval configuration!")
end
if server.empty?
    abort("MISSING server configuration!")
end
if key.empty?
    abort("MISSING key configuration!")
end
if metrics.empty?
    abort("MISSING metrics configuration!")
end

def get_val(json, key)
  returnval = ''
  json['measures'].find do |item|
    returnval = item['value']
    key == item['metric']
  end
  returnval
end

SCHEDULER.every interval, :first_in => 0 do |job|

    res = nil
    puts "server: " + server
    puts "#{server}/api/measures/search?projectKeys=#{key}&metricKeys=#{metrics}"
    uri = URI("#{server}/api/measures/search?projectKeys=#{key}&metricKeys=#{metrics}")

    # if no username is provided, assume sonar server is unsecured
    if username.empty?
        res = Net::HTTP.get(uri)

    else
        Net::HTTP.start(uri.host, uri.port,
          :use_ssl => uri.scheme == 'https', :verify_mode => OpenSSL::SSL::VERIFY_NONE) do |http|

          request = Net::HTTP::Get.new uri.request_uri
          request.basic_auth username, password

          res = http.request request
        end
    end

    # Order of display is dictated by order of metrics in sonar.cfg
    metricsArray = metrics.split(',')
    hashArray = []
    metricsArray.each { |x| hashArray.push({:label => x.gsub("_", " "), :value => get_val(JSON[res.body], x) }) }

    send_event(id, { items: hashArray })
end

No changes expected in sonar.cfg (for example, for metrics i was using, in case keys have changed : duplicated_lines,coverage,blocker_violations,major_violations)
By the way, list of available metrics can now be retrieved (for debugging purpose) through /api/metrics/search

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment