graphite-dashboard-api 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
 - data/.travis.yml +6 -0
 - data/Gemfile +5 -0
 - data/Gemfile.lock +28 -0
 - data/README.md +32 -0
 - data/graphite_dashboard_api.gemspec +20 -0
 - data/lib/graphite-dashboard-api.rb +3 -0
 - data/lib/graphite-dashboard-api/api.rb +54 -0
 - data/lib/graphite-dashboard-api/dashboard.rb +139 -0
 - data/lib/graphite-dashboard-api/graph.rb +85 -0
 - data/spec/dashboard_spec.rb +97 -0
 - data/spec/graph_spec.rb +54 -0
 - metadata +84 -0
 
    
        checksums.yaml
    ADDED
    
    | 
         @@ -0,0 +1,7 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            ---
         
     | 
| 
      
 2 
     | 
    
         
            +
            SHA1:
         
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 026018d8a4341a1ebef7965fec8089f395266064
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 17b8708e169cce2ae098298de0f37ba871876191
         
     | 
| 
      
 5 
     | 
    
         
            +
            SHA512:
         
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 3ed9217aff65cb7e6c300419f9ee322e4d0809cbb431dbe9e6c48c347b4c2f324ddd7a2c0e2fe2b390f40101ff91264686ba0106e9ef77800e92d507de81ffce
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 3eb99d69b1ca9184726d5697b48cf1f30238b247a260801ca4ce06d243e489008b3994239af2b082b6d1ed6a2bd91a6188b7bb83fd8fcfb1871c2f7bed543a79
         
     | 
    
        data/.travis.yml
    ADDED
    
    
    
        data/Gemfile
    ADDED
    
    
    
        data/Gemfile.lock
    ADDED
    
    | 
         @@ -0,0 +1,28 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            PATH
         
     | 
| 
      
 2 
     | 
    
         
            +
              remote: .
         
     | 
| 
      
 3 
     | 
    
         
            +
              specs:
         
     | 
| 
      
 4 
     | 
    
         
            +
                graphite-dashboard-api (0.1.0)
         
     | 
| 
      
 5 
     | 
    
         
            +
                  rest-client
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            GEM
         
     | 
| 
      
 8 
     | 
    
         
            +
              remote: https://rubygems.org/
         
     | 
| 
      
 9 
     | 
    
         
            +
              specs:
         
     | 
| 
      
 10 
     | 
    
         
            +
                diff-lcs (1.2.5)
         
     | 
| 
      
 11 
     | 
    
         
            +
                mime-types (1.25.1)
         
     | 
| 
      
 12 
     | 
    
         
            +
                rest-client (1.6.7)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  mime-types (>= 1.16)
         
     | 
| 
      
 14 
     | 
    
         
            +
                rspec (2.14.1)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  rspec-core (~> 2.14.0)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  rspec-expectations (~> 2.14.0)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  rspec-mocks (~> 2.14.0)
         
     | 
| 
      
 18 
     | 
    
         
            +
                rspec-core (2.14.7)
         
     | 
| 
      
 19 
     | 
    
         
            +
                rspec-expectations (2.14.5)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  diff-lcs (>= 1.1.3, < 2.0)
         
     | 
| 
      
 21 
     | 
    
         
            +
                rspec-mocks (2.14.6)
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
            PLATFORMS
         
     | 
| 
      
 24 
     | 
    
         
            +
              ruby
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            DEPENDENCIES
         
     | 
| 
      
 27 
     | 
    
         
            +
              graphite-dashboard-api!
         
     | 
| 
      
 28 
     | 
    
         
            +
              rspec
         
     | 
    
        data/README.md
    ADDED
    
    | 
         @@ -0,0 +1,32 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            Graphite Dashboard API
         
     | 
| 
      
 2 
     | 
    
         
            +
            ----------------------
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            [](https://travis-ci.org/criteo/graphite-dashboard-api)
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            Graphite dashboard api is a ruby gem which help to create and update graphite dashboards.
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            It allows to create dashboard with code instead of long manipulations in graphite dashboard UI.
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            How to
         
     | 
| 
      
 12 
     | 
    
         
            +
            ------
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            Simple example:
         
     | 
| 
      
 15 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 16 
     | 
    
         
            +
            my_graphs = %w(preprod prod).map do |env|
         
     | 
| 
      
 17 
     | 
    
         
            +
              GraphiteDashboardApi::Graph.new "chef-client run time" do
         
     | 
| 
      
 18 
     | 
    
         
            +
                targets [
         
     | 
| 
      
 19 
     | 
    
         
            +
                  "alias(averageSeries(storage.#{env}.chef.*.elapsed_time),\"average run time\")",
         
     | 
| 
      
 20 
     | 
    
         
            +
                  "alias(maxSeries(storage.#{env}.chef.*.elapsed_time),\"max run time\")"
         
     | 
| 
      
 21 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
            dashboard = GraphiteDashboardApi::Dashboard.new 'chef-run-time' do
         
     | 
| 
      
 26 
     | 
    
         
            +
              graphs my_graphs
         
     | 
| 
      
 27 
     | 
    
         
            +
            end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
            dashboard.save!('http://mygraphite.server.com')
         
     | 
| 
      
 30 
     | 
    
         
            +
            ```
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            For more complex examples, see `spec/` folder
         
     | 
| 
         @@ -0,0 +1,20 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # -*- encoding: utf-8 -*-
         
     | 
| 
      
 2 
     | 
    
         
            +
            $:.push File.expand_path("../lib", __FILE__)
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            Gem::Specification.new do |s|
         
     | 
| 
      
 5 
     | 
    
         
            +
              s.name        = "graphite-dashboard-api"
         
     | 
| 
      
 6 
     | 
    
         
            +
              s.version     = '0.1.0'
         
     | 
| 
      
 7 
     | 
    
         
            +
              s.platform    = Gem::Platform::RUBY
         
     | 
| 
      
 8 
     | 
    
         
            +
              s.authors     = ["Grégoire Seux"]
         
     | 
| 
      
 9 
     | 
    
         
            +
              s.license     = "Apache License v2"
         
     | 
| 
      
 10 
     | 
    
         
            +
              s.summary     = %q{DSL over graphite dashboard api}
         
     | 
| 
      
 11 
     | 
    
         
            +
              s.homepage    = "http://github.com/criteo/graphite-dashboard-api"
         
     | 
| 
      
 12 
     | 
    
         
            +
              s.description = %q{}
         
     | 
| 
      
 13 
     | 
    
         
            +
              s.files         = `git ls-files`.split("\n")
         
     | 
| 
      
 14 
     | 
    
         
            +
              s.test_files    = `git ls-files -- spec/*`.split("\n")
         
     | 
| 
      
 15 
     | 
    
         
            +
              s.executables   = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              s.add_development_dependency 'rspec'
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              s.add_dependency 'rest-client'
         
     | 
| 
      
 20 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,54 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'uri'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'rest_client'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'json'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module GraphiteDashboardApi
         
     | 
| 
      
 6 
     | 
    
         
            +
              module Api
         
     | 
| 
      
 7 
     | 
    
         
            +
                # this mixin requires a from_hash! and to_hash methods + @name
         
     | 
| 
      
 8 
     | 
    
         
            +
                def save!(uri)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  data = encode
         
     | 
| 
      
 10 
     | 
    
         
            +
                  response = rest_request(uri, "save/#{@name}", :post, data)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  response
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                def load!(uri, name = nil)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  response = rest_request(uri, "load/#{name || @name}", :get)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  self.from_hash!(response)
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                def search(uri, pattern)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  response = rest_request(uri, "find/?query=#{pattern}", :get)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  response['dashboards']
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def exists?(uri, name = nil)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  pattern = name || @name
         
     | 
| 
      
 26 
     | 
    
         
            +
                  search(uri, pattern).map { |el| el['name'] }.include? (pattern)
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                private
         
     | 
| 
      
 30 
     | 
    
         
            +
                def rest_request(uri, endpoint, method, payload = nil)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  uri = "#{uri}/dashboard/#{endpoint}"
         
     | 
| 
      
 32 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 33 
     | 
    
         
            +
                    r = RestClient::Request.new(
         
     | 
| 
      
 34 
     | 
    
         
            +
                      url: uri,
         
     | 
| 
      
 35 
     | 
    
         
            +
                      payload: payload,
         
     | 
| 
      
 36 
     | 
    
         
            +
                      method: method,
         
     | 
| 
      
 37 
     | 
    
         
            +
                      timeout: 2,
         
     | 
| 
      
 38 
     | 
    
         
            +
                      open_timeout: 2,
         
     | 
| 
      
 39 
     | 
    
         
            +
                      headers: {
         
     | 
| 
      
 40 
     | 
    
         
            +
                        accept: :json,
         
     | 
| 
      
 41 
     | 
    
         
            +
                        content_type: :json,
         
     | 
| 
      
 42 
     | 
    
         
            +
                      }
         
     | 
| 
      
 43 
     | 
    
         
            +
                    )
         
     | 
| 
      
 44 
     | 
    
         
            +
                    response = r.execute
         
     | 
| 
      
 45 
     | 
    
         
            +
                  rescue => e
         
     | 
| 
      
 46 
     | 
    
         
            +
                    raise "Rest client error: #{e}"
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
                  if response.code != 200 || JSON.parse(response.body)['error']
         
     | 
| 
      
 49 
     | 
    
         
            +
                    fail "Error calling dashboard API: #{response.code} #{response.body}"
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
      
 51 
     | 
    
         
            +
                  JSON.parse(response.body)
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
              end
         
     | 
| 
      
 54 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,139 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module GraphiteDashboardApi
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Dashboard
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
                include Api
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                attr_accessor :name
         
     | 
| 
      
 7 
     | 
    
         
            +
                attr_accessor :graphs
         
     | 
| 
      
 8 
     | 
    
         
            +
                attr_accessor :stringify_graphSize
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def initialize(name = nil, &block)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @name = name
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @graphs = []
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @defaultGraphParams_width  = '800'
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @defaultGraphParams_from   = '-5minutes'
         
     | 
| 
      
 15 
     | 
    
         
            +
                  @defaultGraphParams_until  = 'now'
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @defaultGraphParams_height = '400'
         
     | 
| 
      
 17 
     | 
    
         
            +
                  @refreshConfig_interval =  600000
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @refreshConfig_enabled  = false
         
     | 
| 
      
 19 
     | 
    
         
            +
                  @graphSize_width        = 400
         
     | 
| 
      
 20 
     | 
    
         
            +
                  @graphSize_height       = 250
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @stringify_graphSize    = false # tweaking option
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  # default timeConfig
         
     | 
| 
      
 24 
     | 
    
         
            +
                  @timeConfig_type = 'relative'
         
     | 
| 
      
 25 
     | 
    
         
            +
                  @timeConfig_relativeStartUnits = 'minutes'
         
     | 
| 
      
 26 
     | 
    
         
            +
                  @timeConfig_relativeStartQuantity = '5'
         
     | 
| 
      
 27 
     | 
    
         
            +
                  @timeConfig_relativeUntilUnits = 'now'
         
     | 
| 
      
 28 
     | 
    
         
            +
                  @timeConfig_relativeUntilQuantity = ''
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  @timeConfig_startDate = '1970-01-01T00:00:00'
         
     | 
| 
      
 31 
     | 
    
         
            +
                  @timeConfig_endDate = '1970-01-01T00:05:00'
         
     | 
| 
      
 32 
     | 
    
         
            +
                  @timeConfig_startTime = '9:00 AM'
         
     | 
| 
      
 33 
     | 
    
         
            +
                  @timeConfig_endTime  = '5:00 PM'
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  instance_eval(&block) if block
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                DEFAULT_GRAPH_PARAMS = [:width, :from, :until, :height]
         
     | 
| 
      
 39 
     | 
    
         
            +
                REFRESH_CONFIG = [:interval, :enabled]
         
     | 
| 
      
 40 
     | 
    
         
            +
                TIME_CONFIG = [:startDate, :relativeStartUnits, :endDate, :relativeStartQuantity, :relativeUntilQuantity, :startTime, :endTime, :type, :relativeUntilUnits]
         
     | 
| 
      
 41 
     | 
    
         
            +
                GRAPH_SIZE = [:width, :height]
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                OPTIONS = {
         
     | 
| 
      
 44 
     | 
    
         
            +
                  'defaultGraphParams' => DEFAULT_GRAPH_PARAMS,
         
     | 
| 
      
 45 
     | 
    
         
            +
                  'refreshConfig' => REFRESH_CONFIG,
         
     | 
| 
      
 46 
     | 
    
         
            +
                  'timeConfig' => TIME_CONFIG,
         
     | 
| 
      
 47 
     | 
    
         
            +
                  'graphSize' => GRAPH_SIZE,
         
     | 
| 
      
 48 
     | 
    
         
            +
                }
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                def stringify_graphSize(arg = nil)
         
     | 
| 
      
 51 
     | 
    
         
            +
                  if arg
         
     | 
| 
      
 52 
     | 
    
         
            +
                    @stringify_graphSize = arg
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
                  @stringify_graphSize
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                def graphs(arg = nil)
         
     | 
| 
      
 58 
     | 
    
         
            +
                  if arg
         
     | 
| 
      
 59 
     | 
    
         
            +
                    @graphs = arg
         
     | 
| 
      
 60 
     | 
    
         
            +
                  end
         
     | 
| 
      
 61 
     | 
    
         
            +
                  @graphs
         
     | 
| 
      
 62 
     | 
    
         
            +
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                OPTIONS.each do |k, options|
         
     | 
| 
      
 65 
     | 
    
         
            +
                  options.each do |v|
         
     | 
| 
      
 66 
     | 
    
         
            +
                    accessor_name = k + '_' + v.to_s
         
     | 
| 
      
 67 
     | 
    
         
            +
                    attr_accessor accessor_name
         
     | 
| 
      
 68 
     | 
    
         
            +
                    define_method(accessor_name) do |arg = nil|
         
     | 
| 
      
 69 
     | 
    
         
            +
                    if arg
         
     | 
| 
      
 70 
     | 
    
         
            +
                      instance_variable_set("@#{accessor_name}".to_sym, arg)
         
     | 
| 
      
 71 
     | 
    
         
            +
                    else
         
     | 
| 
      
 72 
     | 
    
         
            +
                      instance_variable_get "@#{accessor_name}".to_sym
         
     | 
| 
      
 73 
     | 
    
         
            +
                    end
         
     | 
| 
      
 74 
     | 
    
         
            +
                    end
         
     | 
| 
      
 75 
     | 
    
         
            +
                    define_method((accessor_name.to_s + '_').to_sym) do |arg = nil|
         
     | 
| 
      
 76 
     | 
    
         
            +
                    send(accessor_name, arg)
         
     | 
| 
      
 77 
     | 
    
         
            +
                    end
         
     | 
| 
      
 78 
     | 
    
         
            +
                  end
         
     | 
| 
      
 79 
     | 
    
         
            +
                end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                def to_hash
         
     | 
| 
      
 82 
     | 
    
         
            +
                  state = {}
         
     | 
| 
      
 83 
     | 
    
         
            +
                  state['name'] = @name
         
     | 
| 
      
 84 
     | 
    
         
            +
                  OPTIONS.each do |k, options|
         
     | 
| 
      
 85 
     | 
    
         
            +
                    state[k] = Hash.new
         
     | 
| 
      
 86 
     | 
    
         
            +
                    options.each do |kk|
         
     | 
| 
      
 87 
     | 
    
         
            +
                      state[k][kk.to_s] = instance_variable_get "@#{k}_#{kk}".to_sym
         
     | 
| 
      
 88 
     | 
    
         
            +
                      state[k][kk.to_s] = state[k][kk.to_s].to_s if (k.eql? 'graphSize' and @stringify_graphSize)
         
     | 
| 
      
 89 
     | 
    
         
            +
                    end
         
     | 
| 
      
 90 
     | 
    
         
            +
                  end
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                  state['graphs'] = @graphs.map do |graph|
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                    [graph.leading_entries, graph.to_hash, graph.url(state['graphSize'])]
         
     | 
| 
      
 95 
     | 
    
         
            +
                  end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                  hash = { 'state' => state }
         
     | 
| 
      
 98 
     | 
    
         
            +
                  hash
         
     | 
| 
      
 99 
     | 
    
         
            +
                end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                def from_hash!(hash)
         
     | 
| 
      
 102 
     | 
    
         
            +
                  state = hash['state']
         
     | 
| 
      
 103 
     | 
    
         
            +
                  @name = state['name']
         
     | 
| 
      
 104 
     | 
    
         
            +
                  OPTIONS.each do |k, options|
         
     | 
| 
      
 105 
     | 
    
         
            +
                    if state[k]
         
     | 
| 
      
 106 
     | 
    
         
            +
                      options.each do |kk|
         
     | 
| 
      
 107 
     | 
    
         
            +
                        value = state[k][kk.to_s]
         
     | 
| 
      
 108 
     | 
    
         
            +
                        instance_variable_set("@#{k}_#{kk}".to_sym, value) if value
         
     | 
| 
      
 109 
     | 
    
         
            +
                      end
         
     | 
| 
      
 110 
     | 
    
         
            +
                    end
         
     | 
| 
      
 111 
     | 
    
         
            +
                  end
         
     | 
| 
      
 112 
     | 
    
         
            +
                  if state['graphs']
         
     | 
| 
      
 113 
     | 
    
         
            +
                    state['graphs'].each do |graph_entry|
         
     | 
| 
      
 114 
     | 
    
         
            +
                      fail 'Graph entry is supposed to have 3 elements' unless graph_entry.size.eql? 3
         
     | 
| 
      
 115 
     | 
    
         
            +
                      graph = graph_entry[1]
         
     | 
| 
      
 116 
     | 
    
         
            +
                      new_graph = Graph.new
         
     | 
| 
      
 117 
     | 
    
         
            +
                      new_graph.from_hash!(graph)
         
     | 
| 
      
 118 
     | 
    
         
            +
                      @graphs << new_graph
         
     | 
| 
      
 119 
     | 
    
         
            +
                    end
         
     | 
| 
      
 120 
     | 
    
         
            +
                  end
         
     | 
| 
      
 121 
     | 
    
         
            +
                  self
         
     | 
| 
      
 122 
     | 
    
         
            +
                end
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                def encode
         
     | 
| 
      
 125 
     | 
    
         
            +
                  json = JSON.generate(to_hash['state'])
         
     | 
| 
      
 126 
     | 
    
         
            +
                  'state=' + URI.encode(json, my_unsafe)
         
     | 
| 
      
 127 
     | 
    
         
            +
                end
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
                def decode(encoded_string)
         
     | 
| 
      
 130 
     | 
    
         
            +
                  json = URI.decode(encoded_string[/=(.*)/, 1], my_unsafe)
         
     | 
| 
      
 131 
     | 
    
         
            +
                  JSON.parse(json)
         
     | 
| 
      
 132 
     | 
    
         
            +
                end
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                def my_unsafe
         
     | 
| 
      
 135 
     | 
    
         
            +
                  Regexp.union(URI::UNSAFE, /[,&+]/)
         
     | 
| 
      
 136 
     | 
    
         
            +
                end
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
              end
         
     | 
| 
      
 139 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,85 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'uri'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module GraphiteDashboardApi
         
     | 
| 
      
 4 
     | 
    
         
            +
              class Graph
         
     | 
| 
      
 5 
     | 
    
         
            +
                PROPS = [:from, :until, :width, :height]
         
     | 
| 
      
 6 
     | 
    
         
            +
                [PROPS, :targets, :titles, :compact_leading].flatten.each do |a|
         
     | 
| 
      
 7 
     | 
    
         
            +
                  attr_accessor a
         
     | 
| 
      
 8 
     | 
    
         
            +
                  define_method(a) do |arg = nil|
         
     | 
| 
      
 9 
     | 
    
         
            +
                  if arg
         
     | 
| 
      
 10 
     | 
    
         
            +
                    instance_variable_set("@#{a}".to_sym, arg)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  else
         
     | 
| 
      
 12 
     | 
    
         
            +
                    instance_variable_get "@#{a}".to_sym
         
     | 
| 
      
 13 
     | 
    
         
            +
                  end
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
                  define_method((a.to_s + '_').to_sym) do |arg = nil|
         
     | 
| 
      
 16 
     | 
    
         
            +
                  send(a, arg)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                def initialize(title = nil, &block)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @title = title
         
     | 
| 
      
 22 
     | 
    
         
            +
                  @targets = []
         
     | 
| 
      
 23 
     | 
    
         
            +
                  @compact_leading = false # this is tweaking stuff
         
     | 
| 
      
 24 
     | 
    
         
            +
                  instance_eval(&block) if block
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                def url(default_options)
         
     | 
| 
      
 28 
     | 
    
         
            +
                  '/render?' + render_options(default_options) + '&' + target_encode + "&title=#{@title || default_title}"
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                # This is probably an over simplification TODO
         
     | 
| 
      
 32 
     | 
    
         
            +
                def default_title
         
     | 
| 
      
 33 
     | 
    
         
            +
                  @targets.first
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                def render_options(default_options)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  opts = []
         
     | 
| 
      
 38 
     | 
    
         
            +
                  PROPS.each do |k|
         
     | 
| 
      
 39 
     | 
    
         
            +
                    v = instance_variable_get "@#{k}".to_sym
         
     | 
| 
      
 40 
     | 
    
         
            +
                    v ||= default_options[k.to_s]
         
     | 
| 
      
 41 
     | 
    
         
            +
                    opts << "#{k.to_s}=#{v}" if v
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
                  opts.join('&')
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                def leading_entries
         
     | 
| 
      
 47 
     | 
    
         
            +
                  if @compact_leading
         
     | 
| 
      
 48 
     | 
    
         
            +
                    leading_entries = target_encode
         
     | 
| 
      
 49 
     | 
    
         
            +
                  else
         
     | 
| 
      
 50 
     | 
    
         
            +
                    leading_entries = @targets
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                def target_encode
         
     | 
| 
      
 55 
     | 
    
         
            +
                  @targets.map do |target|
         
     | 
| 
      
 56 
     | 
    
         
            +
                    'target=' + target
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end.join('&')
         
     | 
| 
      
 58 
     | 
    
         
            +
                end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                def to_hash
         
     | 
| 
      
 61 
     | 
    
         
            +
                  hash = {}
         
     | 
| 
      
 62 
     | 
    
         
            +
                  hash['title'] = @title if @title
         
     | 
| 
      
 63 
     | 
    
         
            +
                  PROPS.each do |k|
         
     | 
| 
      
 64 
     | 
    
         
            +
                    v = instance_variable_get "@#{k}".to_sym
         
     | 
| 
      
 65 
     | 
    
         
            +
                    hash[k.to_s] = v if v
         
     | 
| 
      
 66 
     | 
    
         
            +
                  end
         
     | 
| 
      
 67 
     | 
    
         
            +
                  hash['target'] = @targets
         
     | 
| 
      
 68 
     | 
    
         
            +
                  hash
         
     | 
| 
      
 69 
     | 
    
         
            +
                end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                def from_hash!(hash)
         
     | 
| 
      
 72 
     | 
    
         
            +
                  @title = hash['title']
         
     | 
| 
      
 73 
     | 
    
         
            +
                  PROPS.each do |k|
         
     | 
| 
      
 74 
     | 
    
         
            +
                    value = hash[k.to_s]
         
     | 
| 
      
 75 
     | 
    
         
            +
                    instance_variable_set("@#{k}".to_sym, value) if value
         
     | 
| 
      
 76 
     | 
    
         
            +
                  end
         
     | 
| 
      
 77 
     | 
    
         
            +
                  if hash['target']
         
     | 
| 
      
 78 
     | 
    
         
            +
                    hash['target'].each do |target|
         
     | 
| 
      
 79 
     | 
    
         
            +
                      @targets << target
         
     | 
| 
      
 80 
     | 
    
         
            +
                    end
         
     | 
| 
      
 81 
     | 
    
         
            +
                  end
         
     | 
| 
      
 82 
     | 
    
         
            +
                  self
         
     | 
| 
      
 83 
     | 
    
         
            +
                end
         
     | 
| 
      
 84 
     | 
    
         
            +
              end
         
     | 
| 
      
 85 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,97 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'rspec'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require_relative '../lib/graphite-dashboard-api'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'json'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            describe GraphiteDashboardApi::Dashboard do
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              it 'can generate complex dashboards' do
         
     | 
| 
      
 8 
     | 
    
         
            +
                part1 = 'state=%7B%22name%22:%22chef-storage-automatic%22%2C%22defaultGraphParams%22:%7B%22width%22:%22350%22%2C%22from%22:%22-4days%22%2C%22until%22:%22now%22%2C%22height%22:%22250%22%7D%2C%22refreshConfig%22:%7B%22interval%22:600000%2C%22enabled%22:true%7D%2C%22timeConfig%22:%7B%22startDate%22:%221970-01-01T00:00:00%22%2C%22relativeStartUnits%22:%22days%22%2C%22endDate%22:%221970-01-01T00:05:00%22%2C%22relativeStartQuantity%22:%224%22%2C%22relativeUntilQuantity%22:%22%22%2C%22startTime%22:%229:00%20AM%22%2C%22endTime%22:%225:00%20PM%22%2C%22type%22:%22relative%22%2C%22relativeUntilUnits%22:%22now%22%7D%2C%22graphSize%22:%7B%22width%22:%22350%22%2C%22height%22:%22250%22%7D%2C%22graphs%22:[[[%22cactiStyle(alias(averageSeries(storage.chef.mdb*.elapsed_time)%2C%5C%22mean%20run%20time%5C%22))%22%2C%22alias(dashed(drawAsInfinite(sumSeries(storage.chef.mdb*.fail)))%2C%5C%22fails%5C%22)%22]%2C%7B%22title%22:%22mongo%20pool%22%2C%22from%22:%22-4days%22%2C%22until%22:%22now%22%2C%22target%22:[%22cactiStyle(alias(averageSeries(storage.chef.mdb*.elapsed_time)%2C%5C%22mean%20run%20time%5C%22))%22%2C%22alias(dashed(drawAsInfinite(sumSeries(storage.chef.mdb*.fail)))%2C%5C%22fails%5C%22)%22]%7D%2C%22/render?from=-4days%26until=now%26width=350%26height=250%26target=cactiStyle(alias(averageSeries(storage.chef.mdb*.elapsed_time)%2C%5C%22mean%20run%20time%5C%22))%26target=alias(dashed(drawAsInfinite(sumSeries(storage.chef.mdb*.fail)))%2C%5C%22fails%5C%22)%26title=mongo%20pool%22]%2C[[%22cactiStyle(alias(averageSeries(storage.chef.rmq*.elapsed_time)%2C%5C%22mean%20run%20time%5C%22))%22%2C%22alias(dashed(drawAsInfinite(sumSeries(storage.chef.rmq*.fail)))%2C%5C%22fails%5C%22)%22]%2C%7B%22title%22:%22Rabbit%20pool%22%2C%22from%22:%22-4days%22%2C%22until%22:%22now%22%2C%22target%22:[%22cactiStyle(alias(averageSeries(storage.chef.rmq*.elapsed_time)%2C%5C%22mean%20run%20time%5C%22))%22%2C%22alias(dashed(drawAsInfinite(sumSeries(storage.chef.rmq*.fail)))%2C%5C%22fails%5C%22)%22]%7D%2C%22/render?from=-4days%26until=now%26width=350%26height=250%26target=cactiStyle(alias(averageSeries(storage.chef.rmq*.elapsed_time)%2C%5C%22mean%20run%20time%5C%22))%26target=alias(dashed(drawAsInfinite(sumSeries(storage.chef.rmq*.fail)))%2C%5C%22fails%5C%22)%26title=Rabbit%20pool%22]%2C[[%22cactiStyle(alias(averageSeries(storage.chef.%7Bcouch%2Cmem%7D*.elapsed_time)%2C%5C%22mean%20run%20time%5C%22))%22%2C%22alias(dashed(drawAsInfinite(sumSeries(storage.chef.%7Bcouch%2Cmem%7D*.fail)))%2C%5C%22fails%5C%22)%22]%2C%7B%22title%22:%22couchbase%20pool%22%2C%22from%22:%22-4days%22%2C%22until%22:%22now%22%2C%22target%22:[%22cactiStyle(alias(averageSeries(storage.chef.%7Bcouch%2Cmem%7D*.elapsed_time)%2C%5C%22mean%20run%20time%5C%22))%22%2C%22alias(dashed(drawAsInfinite(sumSeries(storage.chef.%7Bcouch%2Cmem%7D*.fail)))%2C%5C%22fails%5C%22)%22]%7D%2C%22/render?from=-4days%26until=now%26width=350%26height=250%26target=cactiStyle(alias(averageSeries(storage.chef.%7Bcouch%2Cmem%7D*.elapsed_time)'
         
     | 
| 
      
 9 
     | 
    
         
            +
                part2 = '%2C%5C%22mean%20run%20time%5C%22))%26target=alias(dashed(drawAsInfinite(sumSeries(storage.chef.%7Bcouch%2Cmem%7D*.fail)))%2C%5C%22fails%5C%22)%26title=couchbase%20pool%22]%2C[[%22cactiStyle(alias(averageSeries(storage.chef.%7Bkestrel%2Cbungee%7D*.elapsed_time)%2C%5C%22mean%20run%20time%5C%22))%22%2C%22alias(dashed(drawAsInfinite(sumSeries(storage.chef.%7Bkestrel%2Cbungee%7D*.fail)))%2C%5C%22fails%5C%22)%22]%2C%7B%22title%22:%22kestrel%2Bbungee%20pool%22%2C%22from%22:%22-4days%22%2C%22until%22:%22now%22%2C%22target%22:[%22cactiStyle(alias(averageSeries(storage.chef.%7Bkestrel%2Cbungee%7D*.elapsed_time)%2C%5C%22mean%20run%20time%5C%22))%22%2C%22alias(dashed(drawAsInfinite(sumSeries(storage.chef.%7Bkestrel%2Cbungee%7D*.fail)))%2C%5C%22fails%5C%22)%22]%7D%2C%22/render?from=-4days%26until=now%26width=350%26height=250%26target=cactiStyle(alias(averageSeries(storage.chef.%7Bkestrel%2Cbungee%7D*.elapsed_time)%2C%5C%22mean%20run%20time%5C%22))%26target=alias(dashed(drawAsInfinite(sumSeries(storage.chef.%7Bkestrel%2Cbungee%7D*.fail)))%2C%5C%22fails%5C%22)%26title=kestrel%2Bbungee%20pool%22]%2C[[%22cactiStyle(alias(averageSeries(storage.chef.%7Bsquirrel%2Cgraphite%7D*.elapsed_time)%2C%5C%22mean%20run%20time%5C%22))%22%2C%22alias(dashed(drawAsInfinite(sumSeries(storage.chef.%7Bsquirrel%2Cgraphite%7D*.fail)))%2C%5C%22fails%5C%22)%22]%2C%7B%22title%22:%22Graphites%20pool%22%2C%22from%22:%22-4days%22%2C%22until%22:%22now%22%2C%22target%22:[%22cactiStyle(alias(averageSeries(storage.chef.%7Bsquirrel%2Cgraphite%7D*.elapsed_time)%2C%5C%22mean%20run%20time%5C%22))%22%2C%22alias(dashed(drawAsInfinite(sumSeries(storage.chef.%7Bsquirrel%2Cgraphite%7D*.fail)))%2C%5C%22fails%5C%22)%22]%7D%2C%22/render?from=-4days%26until=now%26width=350%26height=250%26target=cactiStyle(alias(averageSeries(storage.chef.%7Bsquirrel%2Cgraphite%7D*.elapsed_time)%2C%5C%22mean%20run%20time%5C%22))%26target=alias(dashed(drawAsInfinite(sumSeries(storage.chef.%7Bsquirrel%2Cgraphite%7D*.fail)))%2C%5C%22fails%5C%22)%26title=Graphites%20pool%22]%2C[[%22hitcount(sumSeries(storage.chef.%7Bkestrel%2Cbungee%7D*.fail)%2C%5C%2230min%5C%22)%22%2C%22hitcount(sumSeries(storage.chef.%7Bgraphite%2Csquirrel%7D*.fail)%2C%5C%2230min%5C%22)%22%2C%22hitcount(sumSeries(storage.chef.rmq*.fail)%2C%5C%2230min%5C%22)%22%2C%22hitcount(sumSeries(storage.chef.%7Bcouch%2Cmem%7D*.fail)%2C%5C%2230min%5C%22)%22%2C%22hitcount(sumSeries(storage.chef.mdb*.fail)%2C%5C%2230min%5C%22)%22%2C%22color(secondYAxis(sumSeries(storage.chef.*.success))%2C%5C%22green%5C%22)%22]%2C%7B%22title%22:%22Sucess%20%26%20failure%22%2C%22from%22:%22-4days%22%2C%22until%22:%22now%22%2C%22target%22:[%22hitcount(sumSeries(storage.chef.%7Bkestrel%2Cbungee%7D*.fail)%2C%5C%2230min%5C%22)%22%2C%22hitcount(sumSeries(storage.chef.%7Bgraphite%2Csquirrel%7D*.fail)%2C%5C%2230min%5C%22)%22%2C%22hitcount(sumSeries(storage.chef.rmq*.fail)%2C%5C%2230min%5C%22)%22%2C%22hitcount(sumSeries(storage.chef.%7Bcouch%2Cmem%7D*.fail)%2C%5C%2230min'
         
     | 
| 
      
 10 
     | 
    
         
            +
                part3 = '%5C%22)%22%2C%22hitcount(sumSeries(storage.chef.mdb*.fail)%2C%5C%2230min%5C%22)%22%2C%22color(secondYAxis(sumSeries(storage.chef.*.success))%2C%5C%22green%5C%22)%22]%7D%2C%22/render?from=-4days%26until=now%26width=350%26height=250%26target=hitcount(sumSeries(storage.chef.%7Bkestrel%2Cbungee%7D*.fail)%2C%5C%2230min%5C%22)%26target=hitcount(sumSeries(storage.chef.%7Bgraphite%2Csquirrel%7D*.fail)%2C%5C%2230min%5C%22)%26target=hitcount(sumSeries(storage.chef.rmq*.fail)%2C%5C%2230min%5C%22)%26target=hitcount(sumSeries(storage.chef.%7Bcouch%2Cmem%7D*.fail)%2C%5C%2230min%5C%22)%26target=hitcount(sumSeries(storage.chef.mdb*.fail)%2C%5C%2230min%5C%22)%26target=color(secondYAxis(sumSeries(storage.chef.*.success))%2C%5C%22green%5C%22)%26title=Sucess%20%26%20failure%22]%2C[[%22keepLastValue(averageSeries(storage.chef.%7Bkestrel%2Cbungee%7D*.updated_resources))%22%2C%22keepLastValue(averageSeries(storage.chef.%7Bgraphite%2Csquirrel%7D%7D*.updated_resources))%22%2C%22keepLastValue(averageSeries(storage.chef.rmq*.updated_resources))%22%2C%22keepLastValue(averageSeries(storage.chef.%7Bcouch%2Cmem%7D*.updated_resources))%22%2C%22keepLastValue(averageSeries(storage.chef.mdb*.updated_resources))%22]%2C%7B%22title%22:%22Updated%20resources%22%2C%22from%22:%22-4days%22%2C%22until%22:%22now%22%2C%22target%22:[%22keepLastValue(averageSeries(storage.chef.%7Bkestrel%2Cbungee%7D*.updated_resources))%22%2C%22keepLastValue(averageSeries(storage.chef.%7Bgraphite%2Csquirrel%7D%7D*.updated_resources))%22%2C%22keepLastValue(averageSeries(storage.chef.rmq*.updated_resources))%22%2C%22keepLastValue(averageSeries(storage.chef.%7Bcouch%2Cmem%7D*.updated_resources))%22%2C%22keepLastValue(averageSeries(storage.chef.mdb*.updated_resources))%22]%7D%2C%22/render?from=-4days%26until=now%26width=350%26height=250%26target=keepLastValue(averageSeries(storage.chef.%7Bkestrel%2Cbungee%7D*.updated_resources))%26target=keepLastValue(averageSeries(storage.chef.%7Bgraphite%2Csquirrel%7D%7D*.updated_resources))%26target=keepLastValue(averageSeries(storage.chef.rmq*.updated_resources))%26target=keepLastValue(averageSeries(storage.chef.%7Bcouch%2Cmem%7D*.updated_resources))%26target=keepLastValue(averageSeries(storage.chef.mdb*.updated_resources))%26title=Updated%20resources%22]]%7D'
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                expected_encoded_string = part1 + part2 + part3 # vim syntaxic coloration does not handle long lines well
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                pool_definitions = {
         
     | 
| 
      
 15 
     | 
    
         
            +
                  'mongo' => %w(mdb),
         
     | 
| 
      
 16 
     | 
    
         
            +
                  'Rabbit' => %w(rmq),
         
     | 
| 
      
 17 
     | 
    
         
            +
                  'couchbase' => %w(couch mem),
         
     | 
| 
      
 18 
     | 
    
         
            +
                  'kestrel+bungee' => %w(kestrel bungee),
         
     | 
| 
      
 19 
     | 
    
         
            +
                  'Graphites' => %w(squirrel graphite),
         
     | 
| 
      
 20 
     | 
    
         
            +
                }
         
     | 
| 
      
 21 
     | 
    
         
            +
                graphs = pool_definitions.map do |pool, prefixes|
         
     | 
| 
      
 22 
     | 
    
         
            +
                  prefixes_ = '{' + prefixes.join(',') + '}'
         
     | 
| 
      
 23 
     | 
    
         
            +
                  prefixes_ = prefixes.first if prefixes.size.eql? 1
         
     | 
| 
      
 24 
     | 
    
         
            +
                  GraphiteDashboardApi::Graph.new "#{pool} pool" do
         
     | 
| 
      
 25 
     | 
    
         
            +
                    from   '-4days'
         
     | 
| 
      
 26 
     | 
    
         
            +
                    until_ 'now'
         
     | 
| 
      
 27 
     | 
    
         
            +
                    targets [
         
     | 
| 
      
 28 
     | 
    
         
            +
                      "cactiStyle(alias(averageSeries(storage.chef.#{prefixes_}*.elapsed_time),\"mean run time\"))",
         
     | 
| 
      
 29 
     | 
    
         
            +
                      "alias(dashed(drawAsInfinite(sumSeries(storage.chef.#{prefixes_}*.fail))),\"fails\")",
         
     | 
| 
      
 30 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
                success_failure = GraphiteDashboardApi::Graph.new 'Sucess & failure' do
         
     | 
| 
      
 34 
     | 
    
         
            +
                  from   '-4days'
         
     | 
| 
      
 35 
     | 
    
         
            +
                  until_ 'now'
         
     | 
| 
      
 36 
     | 
    
         
            +
                  targets [
         
     | 
| 
      
 37 
     | 
    
         
            +
                    "hitcount(sumSeries(storage.chef.{kestrel,bungee}*.fail),\"30min\")",
         
     | 
| 
      
 38 
     | 
    
         
            +
                    "hitcount(sumSeries(storage.chef.{graphite,squirrel}*.fail),\"30min\")",
         
     | 
| 
      
 39 
     | 
    
         
            +
                    "hitcount(sumSeries(storage.chef.rmq*.fail),\"30min\")",
         
     | 
| 
      
 40 
     | 
    
         
            +
                    "hitcount(sumSeries(storage.chef.{couch,mem}*.fail),\"30min\")",
         
     | 
| 
      
 41 
     | 
    
         
            +
                    "hitcount(sumSeries(storage.chef.mdb*.fail),\"30min\")",
         
     | 
| 
      
 42 
     | 
    
         
            +
                    "color(secondYAxis(sumSeries(storage.chef.*.success)),\"green\")"
         
     | 
| 
      
 43 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
                updated_resources = GraphiteDashboardApi::Graph.new 'Updated resources' do
         
     | 
| 
      
 46 
     | 
    
         
            +
                  from   '-4days'
         
     | 
| 
      
 47 
     | 
    
         
            +
                  until_ 'now'
         
     | 
| 
      
 48 
     | 
    
         
            +
                  targets [
         
     | 
| 
      
 49 
     | 
    
         
            +
                    'keepLastValue(averageSeries(storage.chef.{kestrel,bungee}*.updated_resources))',
         
     | 
| 
      
 50 
     | 
    
         
            +
                    'keepLastValue(averageSeries(storage.chef.{graphite,squirrel}}*.updated_resources))',
         
     | 
| 
      
 51 
     | 
    
         
            +
                    'keepLastValue(averageSeries(storage.chef.rmq*.updated_resources))',
         
     | 
| 
      
 52 
     | 
    
         
            +
                    'keepLastValue(averageSeries(storage.chef.{couch,mem}*.updated_resources))',
         
     | 
| 
      
 53 
     | 
    
         
            +
                    'keepLastValue(averageSeries(storage.chef.mdb*.updated_resources))'
         
     | 
| 
      
 54 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                dashboard = GraphiteDashboardApi::Dashboard.new 'chef-storage-automatic' do
         
     | 
| 
      
 58 
     | 
    
         
            +
                  defaultGraphParams_width '350'
         
     | 
| 
      
 59 
     | 
    
         
            +
                  defaultGraphParams_height '250'
         
     | 
| 
      
 60 
     | 
    
         
            +
                  graphSize_width            350
         
     | 
| 
      
 61 
     | 
    
         
            +
                  stringify_graphSize        true
         
     | 
| 
      
 62 
     | 
    
         
            +
                  defaultGraphParams_from   '-4days'
         
     | 
| 
      
 63 
     | 
    
         
            +
                  timeConfig_relativeStartUnits 'days'
         
     | 
| 
      
 64 
     | 
    
         
            +
                  timeConfig_relativeStartQuantity '4'
         
     | 
| 
      
 65 
     | 
    
         
            +
                  refreshConfig_enabled true
         
     | 
| 
      
 66 
     | 
    
         
            +
                  graphs ([graphs, success_failure, updated_resources].flatten)
         
     | 
| 
      
 67 
     | 
    
         
            +
                end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                expected_json = { 'state' => JSON.parse(URI.decode(expected_encoded_string)[/^state=(.*)/, 1]) }
         
     | 
| 
      
 70 
     | 
    
         
            +
                comparison_by_block(dashboard, expected_json)
         
     | 
| 
      
 71 
     | 
    
         
            +
                # expect(dashboard.save!(a_graphite_url)['success']).to eq true
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
              end
         
     | 
| 
      
 74 
     | 
    
         
            +
            end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
            def comparison_by_block(dashboard, expected_json)
         
     | 
| 
      
 77 
     | 
    
         
            +
              result = JSON.parse(JSON.generate(dashboard.to_hash))['state']
         
     | 
| 
      
 78 
     | 
    
         
            +
              expected_json['state'].each do |k, v|
         
     | 
| 
      
 79 
     | 
    
         
            +
                expect(result[k]).to eq v
         
     | 
| 
      
 80 
     | 
    
         
            +
              end
         
     | 
| 
      
 81 
     | 
    
         
            +
              expect(JSON.parse(JSON.generate(dashboard.to_hash))).to eq expected_json
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
              # also check idempotency of (to_hash o from_hash)
         
     | 
| 
      
 84 
     | 
    
         
            +
              # we have to be careful with tweaking options
         
     | 
| 
      
 85 
     | 
    
         
            +
              new_dashboard = GraphiteDashboardApi::Dashboard.new
         
     | 
| 
      
 86 
     | 
    
         
            +
              new_dashboard.from_hash! dashboard.to_hash
         
     | 
| 
      
 87 
     | 
    
         
            +
              expect(new_dashboard.graphs.size).to eq dashboard.graphs.size
         
     | 
| 
      
 88 
     | 
    
         
            +
              dashboard.graphs.each_with_index do |el, index|
         
     | 
| 
      
 89 
     | 
    
         
            +
                if el.compact_leading
         
     | 
| 
      
 90 
     | 
    
         
            +
                  new_dashboard.graphs[index].compact_leading = true
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
              end
         
     | 
| 
      
 93 
     | 
    
         
            +
              new_dashboard.to_hash['state'].each do |k, v|
         
     | 
| 
      
 94 
     | 
    
         
            +
                expect(dashboard.to_hash['state'][k]).to eq v
         
     | 
| 
      
 95 
     | 
    
         
            +
              end
         
     | 
| 
      
 96 
     | 
    
         
            +
              expect(new_dashboard.to_hash).to eq dashboard.to_hash
         
     | 
| 
      
 97 
     | 
    
         
            +
            end
         
     | 
    
        data/spec/graph_spec.rb
    ADDED
    
    | 
         @@ -0,0 +1,54 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'rspec'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require_relative '../lib/graphite-dashboard-api'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'json'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            describe GraphiteDashboardApi::Graph do
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              let(:graph_with_title) do
         
     | 
| 
      
 8 
     | 
    
         
            +
                graph = GraphiteDashboardApi::Graph.new 'title' do
         
     | 
| 
      
 9 
     | 
    
         
            +
                  from '-5minutes'
         
     | 
| 
      
 10 
     | 
    
         
            +
                  until_ 'now'
         
     | 
| 
      
 11 
     | 
    
         
            +
                  targets ['storage.rsyslog.action1.processed.kestrel01-am5']
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
                expected_json = JSON.parse('{"until": "now","from": "-5minutes","target": ["storage.rsyslog.action1.processed.kestrel01-am5"],"title": "title"}')
         
     | 
| 
      
 14 
     | 
    
         
            +
                [graph, expected_json]
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              let(:graph_without_title) do
         
     | 
| 
      
 18 
     | 
    
         
            +
                graph = GraphiteDashboardApi::Graph.new do
         
     | 
| 
      
 19 
     | 
    
         
            +
                  from '-5minutes'
         
     | 
| 
      
 20 
     | 
    
         
            +
                  until_ 'now'
         
     | 
| 
      
 21 
     | 
    
         
            +
                  targets ['storage.rsyslog.action1.processed.kestrel01-am5']
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
                expected_json = JSON.parse('{"until": "now","from": "-5minutes","target": ["storage.rsyslog.action1.processed.kestrel01-am5"]}')
         
     | 
| 
      
 24 
     | 
    
         
            +
                [graph, expected_json]
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
              it 'can be constructed' do
         
     | 
| 
      
 28 
     | 
    
         
            +
                graph = GraphiteDashboardApi::Graph.new('Test graph') do
         
     | 
| 
      
 29 
     | 
    
         
            +
                  from '-4hours'
         
     | 
| 
      
 30 
     | 
    
         
            +
                  until_ '-2hours'
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                expect(graph.from).to eq('-4hours')
         
     | 
| 
      
 34 
     | 
    
         
            +
                expect(graph.until).to eq('-2hours')
         
     | 
| 
      
 35 
     | 
    
         
            +
              end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
              it 'can be converted to json' do
         
     | 
| 
      
 38 
     | 
    
         
            +
                graph, expected_json = graph_with_title
         
     | 
| 
      
 39 
     | 
    
         
            +
                expect(JSON.parse(JSON.generate(graph.to_hash))).to eq expected_json
         
     | 
| 
      
 40 
     | 
    
         
            +
                # also check idempotency of (to_hash o from_hash)
         
     | 
| 
      
 41 
     | 
    
         
            +
                new_graph = GraphiteDashboardApi::Graph.new
         
     | 
| 
      
 42 
     | 
    
         
            +
                expect((new_graph.from_hash! graph.to_hash).to_hash).to eq graph.to_hash
         
     | 
| 
      
 43 
     | 
    
         
            +
              end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
              it 'can be converted to json when no title' do
         
     | 
| 
      
 46 
     | 
    
         
            +
                graph, expected_json = graph_without_title
         
     | 
| 
      
 47 
     | 
    
         
            +
                expect(graph.target_encode).to eq 'target=storage.rsyslog.action1.processed.kestrel01-am5'
         
     | 
| 
      
 48 
     | 
    
         
            +
                expect(JSON.parse(JSON.generate(graph.to_hash))).to eq expected_json
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                # also check idempotency of (to_hash o from_hash)
         
     | 
| 
      
 51 
     | 
    
         
            +
                new_graph = GraphiteDashboardApi::Graph.new
         
     | 
| 
      
 52 
     | 
    
         
            +
                expect((new_graph.from_hash! graph.to_hash).to_hash).to eq graph.to_hash
         
     | 
| 
      
 53 
     | 
    
         
            +
              end
         
     | 
| 
      
 54 
     | 
    
         
            +
            end
         
     | 
    
        metadata
    ADDED
    
    | 
         @@ -0,0 +1,84 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            --- !ruby/object:Gem::Specification
         
     | 
| 
      
 2 
     | 
    
         
            +
            name: graphite-dashboard-api
         
     | 
| 
      
 3 
     | 
    
         
            +
            version: !ruby/object:Gem::Version
         
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.1.0
         
     | 
| 
      
 5 
     | 
    
         
            +
            platform: ruby
         
     | 
| 
      
 6 
     | 
    
         
            +
            authors:
         
     | 
| 
      
 7 
     | 
    
         
            +
            - Grégoire Seux
         
     | 
| 
      
 8 
     | 
    
         
            +
            autorequire: 
         
     | 
| 
      
 9 
     | 
    
         
            +
            bindir: bin
         
     | 
| 
      
 10 
     | 
    
         
            +
            cert_chain: []
         
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2014-02-26 00:00:00.000000000 Z
         
     | 
| 
      
 12 
     | 
    
         
            +
            dependencies:
         
     | 
| 
      
 13 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 14 
     | 
    
         
            +
              name: rspec
         
     | 
| 
      
 15 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 16 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 17 
     | 
    
         
            +
                - - '>='
         
     | 
| 
      
 18 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 19 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 20 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 21 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 22 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 23 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 24 
     | 
    
         
            +
                - - '>='
         
     | 
| 
      
 25 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 26 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 27 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 28 
     | 
    
         
            +
              name: rest-client
         
     | 
| 
      
 29 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 30 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 31 
     | 
    
         
            +
                - - '>='
         
     | 
| 
      
 32 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 33 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 34 
     | 
    
         
            +
              type: :runtime
         
     | 
| 
      
 35 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 36 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 37 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 38 
     | 
    
         
            +
                - - '>='
         
     | 
| 
      
 39 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 40 
     | 
    
         
            +
                    version: '0'
         
     | 
| 
      
 41 
     | 
    
         
            +
            description: ''
         
     | 
| 
      
 42 
     | 
    
         
            +
            email: 
         
     | 
| 
      
 43 
     | 
    
         
            +
            executables: []
         
     | 
| 
      
 44 
     | 
    
         
            +
            extensions: []
         
     | 
| 
      
 45 
     | 
    
         
            +
            extra_rdoc_files: []
         
     | 
| 
      
 46 
     | 
    
         
            +
            files:
         
     | 
| 
      
 47 
     | 
    
         
            +
            - .travis.yml
         
     | 
| 
      
 48 
     | 
    
         
            +
            - Gemfile
         
     | 
| 
      
 49 
     | 
    
         
            +
            - Gemfile.lock
         
     | 
| 
      
 50 
     | 
    
         
            +
            - README.md
         
     | 
| 
      
 51 
     | 
    
         
            +
            - graphite_dashboard_api.gemspec
         
     | 
| 
      
 52 
     | 
    
         
            +
            - lib/graphite-dashboard-api.rb
         
     | 
| 
      
 53 
     | 
    
         
            +
            - lib/graphite-dashboard-api/api.rb
         
     | 
| 
      
 54 
     | 
    
         
            +
            - lib/graphite-dashboard-api/dashboard.rb
         
     | 
| 
      
 55 
     | 
    
         
            +
            - lib/graphite-dashboard-api/graph.rb
         
     | 
| 
      
 56 
     | 
    
         
            +
            - spec/dashboard_spec.rb
         
     | 
| 
      
 57 
     | 
    
         
            +
            - spec/graph_spec.rb
         
     | 
| 
      
 58 
     | 
    
         
            +
            homepage: http://github.com/criteo/graphite-dashboard-api
         
     | 
| 
      
 59 
     | 
    
         
            +
            licenses:
         
     | 
| 
      
 60 
     | 
    
         
            +
            - Apache License v2
         
     | 
| 
      
 61 
     | 
    
         
            +
            metadata: {}
         
     | 
| 
      
 62 
     | 
    
         
            +
            post_install_message: 
         
     | 
| 
      
 63 
     | 
    
         
            +
            rdoc_options: []
         
     | 
| 
      
 64 
     | 
    
         
            +
            require_paths:
         
     | 
| 
      
 65 
     | 
    
         
            +
            - lib
         
     | 
| 
      
 66 
     | 
    
         
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         
     | 
| 
      
 67 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 68 
     | 
    
         
            +
              - - '>='
         
     | 
| 
      
 69 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 70 
     | 
    
         
            +
                  version: '0'
         
     | 
| 
      
 71 
     | 
    
         
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         
     | 
| 
      
 72 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 73 
     | 
    
         
            +
              - - '>='
         
     | 
| 
      
 74 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 75 
     | 
    
         
            +
                  version: '0'
         
     | 
| 
      
 76 
     | 
    
         
            +
            requirements: []
         
     | 
| 
      
 77 
     | 
    
         
            +
            rubyforge_project: 
         
     | 
| 
      
 78 
     | 
    
         
            +
            rubygems_version: 2.0.3
         
     | 
| 
      
 79 
     | 
    
         
            +
            signing_key: 
         
     | 
| 
      
 80 
     | 
    
         
            +
            specification_version: 4
         
     | 
| 
      
 81 
     | 
    
         
            +
            summary: DSL over graphite dashboard api
         
     | 
| 
      
 82 
     | 
    
         
            +
            test_files:
         
     | 
| 
      
 83 
     | 
    
         
            +
            - spec/dashboard_spec.rb
         
     | 
| 
      
 84 
     | 
    
         
            +
            - spec/graph_spec.rb
         
     |