Back to Repositories

Testing Service Discovery Helper Implementation in Fluentd

This test suite validates the Service Discovery Helper functionality in Fluentd, focusing on service configuration management and dynamic discovery features. It ensures proper initialization, configuration, and lifecycle management of service discovery components.

Test Coverage Overview

The test suite provides comprehensive coverage of the ServiceDiscoveryHelper implementation in Fluentd.

  • Service manager creation and configuration validation
  • Dynamic and static service discovery scenarios
  • Service selection and management functionality
  • Error handling and cleanup processes

Implementation Analysis

The testing approach utilizes minitest with FlexMock for robust service discovery testing.

  • Mock objects for DNS resolution testing
  • Event loop validation for async operations
  • Service configuration verification
  • Lifecycle management testing

Technical Details

  • Test framework: Minitest with FlexMock integration
  • Setup includes temporary file directory management
  • Uses dummy plugin classes for isolation
  • Implements teardown procedures for cleanup
  • Includes DNS resolver mocking

Best Practices Demonstrated

The test suite exemplifies high-quality testing practices for plugin helper functionality.

  • Proper test isolation and setup/teardown
  • Comprehensive lifecycle testing
  • Mock object usage for external dependencies
  • Clear test case organization
  • Thorough error condition handling

fluent/fluentd

test/plugin_helper/test_service_discovery.rb

            
require_relative '../helper'
require 'flexmock/test_unit'
require 'fluent/plugin_helper/service_discovery'
require 'fluent/plugin/output'

class ServiceDiscoveryHelper < Test::Unit::TestCase
  class Dummy < Fluent::Plugin::TestBase
    helpers :service_discovery

    # Make these mehtod public
    def service_discovery_create_manager(title, configurations:, load_balancer: nil, custom_build_method: nil, interval: 3)
      super
    end

    def discovery_manager
      super
    end
  end

  class DummyPlugin < Fluent::Plugin::TestBase
    helpers :service_discovery

    def configure(conf)
      super
      service_discovery_configure(:service_discovery_helper_test, static_default_service_directive: 'node')
    end

    def select_service(&block)
      service_discovery_select_service(&block)
    end

    # Make these mehtod public
    def discovery_manager
      super
    end
  end

  setup do
    @sd_file_dir = File.expand_path('../plugin/data/sd_file', __dir__)

    @d = nil
  end

  teardown do
    if @d
      @d.stop unless @d.stopped?
      @d.shutdown unless @d.shutdown?
      @d.after_shutdown unless @d.after_shutdown?
      @d.close unless @d.closed?
      @d.terminate unless @d.terminated?
    end
  end

  test 'support calling #service_discovery_create_manager and #discovery_manager from plugin' do
    d = @d = Dummy.new

    d.service_discovery_create_manager(
      :service_discovery_helper_test,
      configurations: [{ type: :static, conf: config_element('root', '', {}, [config_element('service', '', { 'host' => '127.0.0.1', 'port' => '1234' })]) }],
    )

    assert_true !!d.discovery_manager

    mock.proxy(d.discovery_manager).start.once
    mock.proxy(d).timer_execute(:service_discovery_helper_test, anything).never

    d.start
    d.event_loop_wait_until_start

    services = d.discovery_manager.services
    assert_equal 1, services.size
    assert_equal '127.0.0.1', services[0].host
    assert_equal 1234, services[0].port
  end

  test 'start discovery manager' do
    d = @d = DummyPlugin.new

    services = [config_element('service', '', { 'host' => '127.0.0.1', 'port' => '1234' })]
    d.configure(config_element('root', '', {}, [config_element('service_discovery', '', {'@type' => 'static'}, services)]))

    assert_true !!d.discovery_manager

    mock.proxy(d.discovery_manager).start.once
    mock.proxy(d).timer_execute(:service_discovery_helper_test, anything).never

    d.start
    d.event_loop_wait_until_start

    assert_equal 1, d.discovery_manager.services.size
    d.select_service do |serv|
      assert_equal "127.0.0.1", serv.host
      assert_equal 1234, serv.port
    end
  end

  test 'call timer_execute if dynamic configuration' do
    d = @d = DummyPlugin.new
    d.configure(config_element('root', '', {}, [config_element('service_discovery', '', { '@type' => 'file', 'path' => File.join(@sd_file_dir, 'config.yml' )})]))

    assert_true !!d.discovery_manager
    mock.proxy(d.discovery_manager).start.once
    mock(d).timer_execute(:service_discovery_helper_test, anything).once
    d.start
    d.event_loop_wait_until_start
  end

  test 'exits service discovery instances without any errors' do
    d = @d = DummyPlugin.new
    mockv = flexmock('dns_resolver', getaddress: '127.0.0.1')
              .should_receive(:getresources)
              .and_return([Resolv::DNS::Resource::IN::SRV.new(1, 10, 8081, 'service1.example.com')])
              .mock
    mock(Resolv::DNS).new { mockv }

    d.configure(config_element('root', '', {}, [config_element('service_discovery', '', { '@type' => 'srv', 'service' => 'service1', 'hostname' => 'example.com' })]))

    assert_true !!d.discovery_manager
    mock.proxy(d.discovery_manager).start.once
    mock(d).timer_execute(:service_discovery_helper_test, anything).once

    # To avoid claring `@logs` during `terminate` step
    # https://github.com/fluent/fluentd/blob/bc78d889f93dad8c2a4e0ad1ca802546185dacba/lib/fluent/test/log.rb#L33
    mock(d.log).reset.times(3)

    d.start
    d.event_loop_wait_until_start

    d.stop unless d.stopped?
    d.shutdown unless d.shutdown?
    d.after_shutdown unless d.after_shutdown?
    d.close unless d.closed?
    d.terminate unless d.terminated?

    assert_false(d.log.out.logs.any? { |e| e.match?(/thread doesn't exit correctly/) })
  end

  test 'static service discovery will be configured automatically when default service directive is specified' do
    d = @d = DummyPlugin.new

    nodes = [
      config_element('node', '', { 'host' => '192.168.0.1', 'port' => '24224' }),
      config_element('node', '', { 'host' => '192.168.0.2', 'port' => '24224' })
    ]
    d.configure(config_element('root', '', {}, nodes))

    assert_true !!d.discovery_manager

    mock.proxy(d.discovery_manager).start.once
    mock.proxy(d).timer_execute(:service_discovery_helper_test, anything).never

    d.start
    d.event_loop_wait_until_start

    assert_equal 2, d.discovery_manager.services.size
    d.select_service do |serv|
      assert_equal "192.168.0.1", serv.host
      assert_equal 24224, serv.port
    end
    d.select_service do |serv|
      assert_equal "192.168.0.2", serv.host
      assert_equal 24224, serv.port
    end
  end
end