sensu/sensu-redis

View on GitHub
lib/sensu/redis.rb

Summary

Maintainability
A
0 mins
Test Coverage
require "rubygems"
require "sensu/redis/utilities"
require "sensu/redis/client"
require "sensu/redis/sentinel"
require "eventmachine"
require "uri"

module Sensu
  module Redis
    class << self
      include Utilities

      # Set the Redis logger.
      #
      # @param logger [Object] Redis logger.
      def logger=(logger)
        @logger = logger
      end

      # Parse a Redis URL. An argument error exception is thrown if
      # this method is unable to parse the provided URL string.
      #
      # @param url [String]
      # @return [Hash] Redis connection options.
      def parse_url(url)
        begin
          uri = URI.parse(url)
          {
            :host => uri.host,
            :port => uri.port,
            :password => uri.password
          }
        rescue
          raise ArgumentError, "invalid redis url"
        end
      end

      # Connect to the current Redis master via Sentinel. Sentinel
      # will resolve the current Redis master host and port.
      #
      # @param options [Hash]
      # @yield callback to be called with the redis connection object.
      def connect_via_sentinel(options, &block)
        sentinel = Sentinel.new(options)
        sentinel.logger = @logger
        sentinel.resolve do |host, port|
          redis = EM.connect(host, port, Client, options)
          redis.logger = @logger
          redis.sentinel = sentinel
          block.call(redis)
        end
      end

      # Connect to the current Redis master directly, using the
      # provided connection options. This method uses `resolve_host()`
      # to first resolve the provided host, if it's not already an IP
      # address. Resolving the hostname upfront guards against lookup
      # failures that would cause the Sensu process to crash. Upfront
      # hostname resolution also allows this Redis library to work
      # with Amazon AWS ElastiCache & where DNS is used as a failover
      # mechanism.
      #
      # @param options [Hash]
      # @yield callback to be called with the redis connection object.
      def connect_direct(options, &block)
        resolve_host(options[:host]) do |ip_address|
          if ip_address.nil?
            EM::Timer.new(1) do
              connect_direct(options, &block)
            end
          else
            redis = EM.connect(ip_address, options[:port], Client, options)
            redis.logger = @logger
            block.call(redis)
          end
        end
      end

      # Connect to Redis using the provided connection options.
      #
      # @param options [String,Hash]
      # @yield callback to be called with the redis connection object.
      def connect(options={}, &block)
        case options
        when String
          options = parse_url(options)
        when nil
          options = {}
        end
        options[:host] ||= "127.0.0.1"
        options[:port] ||= 6379
        case
        when options[:sentinels].is_a?(String)
          raw_urls = options[:sentinels]
          options[:sentinels] = raw_urls.split(',').map { |url| parse_url(url) }
        when options[:sentinels].is_a?(Array) && options[:sentinels].length > 0
          connect_via_sentinel(options, &block)
        else
          connect_direct(options, &block)
        end
      end
    end
  end
end