class GrafanaSync::Stage
Constants
- CREDENTIALS
- DASHBOARDS_ROOT
- FILE_ENCODING
- FILE_EXTENSION
Public Class Methods
new(stage:, make_folders: false, debug: false, logger: Logger.new(STDERR, level: Logger::INFO))
click to toggle source
# File lib/grafana_sync/stage.rb, line 18 def initialize(stage:, make_folders: false, debug: false, logger: Logger.new(STDERR, level: Logger::INFO)) @stage_name = stage @make_folders = make_folders @logger = logger HttpLog.configure do |config| config.logger = logger config.log_connect = false config.log_data = debug config.log_response = debug config.log_benchmark = false end GrafanaSync.load_config() validate_config! @base_url = stage_config[:url].chomp("/") folder = stage_config[:folder] # If there's no folder specified in Grafana dashboard config then it's "General". @folder_name = (folder == "General") ? nil : folder end
Public Instance Methods
diff()
click to toggle source
# File lib/grafana_sync/stage.rb, line 64 def diff dashboards_to_delete.keys.each do |key| puts("--- #{@stage_name}/#{key}") puts("+++ /dev/null") puts end dashboards_to_update.keys.sort.each do |key| diff_str = Diffy::Diff.new(JSON.pretty_generate(remote_dashboards[key]), JSON.pretty_generate(local_dashboards[key]), context: 3, diff: '-w', include_diff_info: true).to_s(:color) unless diff_str.chop.empty? puts("--- #{@stage_name}/#{key}") puts("+++ local/#{key}") puts(diff_str) end end end
pull()
click to toggle source
# File lib/grafana_sync/stage.rb, line 39 def pull FileUtils.mkdir_p(DASHBOARDS_ROOT) FileUtils.rm(dashboard_files) remote_dashboards.each do |title, db| @logger.info("Saving dashboard '#{title}'") IO.write(File.join(DASHBOARDS_ROOT, title+FILE_EXTENSION), JSON.pretty_generate(db), encoding: FILE_ENCODING, mode: "w") end end
push()
click to toggle source
# File lib/grafana_sync/stage.rb, line 49 def push dashboards_to_delete.each do |title, _db| @logger.info("Deleting dashboard '#{title}'") uid = dashboard_uid(title) http_delete("/api/dashboards/uid/#{uid}") end dashboards_to_update.each do |title, db| db["folderId"] = (folder_id or make_folder) db["overwrite"] = true @logger.info("Updating dashboard '#{title}'") http_post("/api/dashboards/db", json: db) end end
Private Instance Methods
ask_credentials()
click to toggle source
# File lib/grafana_sync/stage.rb, line 238 def ask_credentials {login: ask_input("User: "), password: ask_input("Password: ", hide: true)}.tap do |cred_hash| IO.write(CREDENTIALS, JSON.pretty_generate(cred_hash), encoding: FILE_ENCODING, mode: "w") end end
ask_input(invitation, hide: false)
click to toggle source
# File lib/grafana_sync/stage.rb, line 247 def ask_input(invitation, hide: false) print(invitation) if hide STDIN.noecho(&:gets).tap { puts } else STDIN.gets end.strip end
credentials()
click to toggle source
# File lib/grafana_sync/stage.rb, line 226 def credentials @credentials ||= load_credentials or ask_credentials end
dashboard_files()
click to toggle source
# File lib/grafana_sync/stage.rb, line 176 def dashboard_files # dashboards/*.json Dir.glob(File.join(DASHBOARDS_ROOT, '*'+FILE_EXTENSION)) end
dashboard_uid(title)
click to toggle source
# File lib/grafana_sync/stage.rb, line 126 def dashboard_uid(title) index.find {|item| item["type"]=="dash-db" and item["title"]==title} .try {|item| item["uid"]}.tap {|val| die("Dashboard #{title} not found on #{@stage_name}!") if val.nil? } end
dashboard_uids()
click to toggle source
# File lib/grafana_sync/stage.rb, line 133 def dashboard_uids index.filter {|item| item["type"]=="dash-db" and item["folderTitle"]==@folder_name} .map {|item| item["uid"]}.tap {|val| @logger.warn("No dashboards found on #{@stage_name}!") if val.empty? } end
dashboards_to_delete()
click to toggle source
# File lib/grafana_sync/stage.rb, line 107 def dashboards_to_delete remote_dashboards.slice(*(remote_dashboards.keys - local_dashboards.keys)) end
dashboards_to_update()
click to toggle source
# File lib/grafana_sync/stage.rb, line 111 def dashboards_to_update local_dashboards end
folder_id()
click to toggle source
# File lib/grafana_sync/stage.rb, line 140 def folder_id @folder_id ||= if @folder_name.nil? 0 # "General" folder ID is always 0. else index.find {|item| item["type"]=="dash-folder" and item["title"]==@folder_name }.try {|item| item["id"] } end end
http()
click to toggle source
# File lib/grafana_sync/stage.rb, line 221 def http @http ||= HTTP.basic_auth(user: credentials[:login], pass: credentials[:password]).follow end
http_delete(path, json: {})
click to toggle source
# File lib/grafana_sync/stage.rb, line 214 def http_delete(path, json: {}) url = @base_url + path response = http.delete(url, json: json) die("Failed to DELETE #{url}!") if response.code != 200 JSON.parse(response.to_s) end
http_get(path)
click to toggle source
# File lib/grafana_sync/stage.rb, line 200 def http_get(path) url = @base_url + path response = http.get(url) die("Failed to GET #{url}!") if response.code != 200 JSON.parse(response.to_s) end
http_post(path, json: {})
click to toggle source
# File lib/grafana_sync/stage.rb, line 207 def http_post(path, json: {}) url = @base_url + path response = http.post(url, json: json) die("Failed to POST #{url}!") if response.code != 200 JSON.parse(response.to_s) end
index()
click to toggle source
# File lib/grafana_sync/stage.rb, line 196 def index @index ||= http_get("/api/search") end
invalidate_index()
click to toggle source
# File lib/grafana_sync/stage.rb, line 161 def invalidate_index @index = nil end
load_credentials()
click to toggle source
# File lib/grafana_sync/stage.rb, line 230 def load_credentials if File.exist?(CREDENTIALS) JSON.parse(IO.read(CREDENTIALS, encoding: FILE_ENCODING)).transform_keys(&:to_sym) else nil end end
local_dashboards()
click to toggle source
# File lib/grafana_sync/stage.rb, line 165 def local_dashboards @local_dashboards ||= dashboard_files.map do |path| @logger.debug("Loading '#{path}'") db = JSON.parse(IO.read(path, encoding: FILE_ENCODING)) title = db["dashboard"]["title"] next if stage_config[:exclude].try { |array| array.include?(title) } replace_datasources!(db) [title, db] end.compact.to_h end
make_folder()
click to toggle source
# File lib/grafana_sync/stage.rb, line 150 def make_folder die("""There is no folder '#{@folder_name}' for '#{@stage_name}'! To create it add --make-folders option to command-line.""") unless @make_folders @logger.info("Making Grafana folder '#{@folder_name}'") response = http_post("/api/folders", json: {title: @folder_name}) response["id"].tap { invalidate_index } end
remote_dashboards()
click to toggle source
# File lib/grafana_sync/stage.rb, line 115 def remote_dashboards @remote_dashboards ||= dashboard_uids.lazy.map do |uid| db = http_get("/api/dashboards/uid/#{uid}") db.delete("meta") ["id", "uid", "version"].each {|key| db["dashboard"].delete(key) } [db["dashboard"]["title"], db] end.to_h end
replace_datasources!(obj)
click to toggle source
# File lib/grafana_sync/stage.rb, line 181 def replace_datasources!(obj) replaces = stage_config[:datasource_replace] return if replaces.nil? if obj.is_a?(Hash) datasource = obj["datasource"] if datasource obj["datasource"] = replaces.fetch(datasource, datasource) end obj.each_value {|value| replace_datasources!(value)} elsif obj.is_a?(Array) obj.each {|value| replace_datasources!(value)} end end
stage_config()
click to toggle source
# File lib/grafana_sync/stage.rb, line 103 def stage_config config[@stage_name] end
validate_config!()
click to toggle source
# File lib/grafana_sync/stage.rb, line 85 def validate_config! unless config.has_key?(@stage_name) die("There's no environment ':#{@stage_name}' defined in config.rb!") end config.keys.each do |stage| [:url, :folder].each {|key| die("config.rb has no :#{key} specified for :#{stage}!") if config[stage][key].nil? } config[stage][:datasource_replace].try do |ds_hash| if ds_hash.has_key?(nil) or ds_hash.has_value?(nil) die("config.rb:#{stage}: nil value in :datasource_replace is not supported!") end end end end