From c5d685c29f94a3c0174fd80ca9b92b7780750be6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathias=20S=C3=B6derberg?= Date: Sat, 31 May 2014 12:53:14 +0200 Subject: [PATCH] Add (experimental) support for systemd as an `init_style` First stab at getting `systemd` to work. Still not sure about a few things such as using an EnvironmentFile and the code for setting up everything related to init styles is even messier, so it could probably use some cleanup and rethinking. But hey, it works. --- .kitchen.yml | 41 ++++- libraries/kafka_helpers.rb | 9 ++ recipes/_configure.rb | 2 +- spec/recipes/configure_spec.rb | 40 ++++- spec/support/matchers.rb | 2 +- templates/default/systemd/default.erb | 8 + templates/default/systemd/kafka.env.erb | 12 ++ .../serverspec/localhost/binary_spec.rb | 8 + .../localhost/required_files_spec.rb | 49 ++++++ .../serverspec/localhost/service_spec.rb | 150 ++++++++++++++++++ 10 files changed, 307 insertions(+), 14 deletions(-) create mode 100644 templates/default/systemd/default.erb create mode 100644 templates/default/systemd/kafka.env.erb create mode 100644 test/integration/init-style-systemd/serverspec/localhost/binary_spec.rb create mode 100644 test/integration/init-style-systemd/serverspec/localhost/required_files_spec.rb create mode 100644 test/integration/init-style-systemd/serverspec/localhost/service_spec.rb diff --git a/.kitchen.yml b/.kitchen.yml index ba27c1fa..4525f829 100644 --- a/.kitchen.yml +++ b/.kitchen.yml @@ -36,10 +36,8 @@ suites: run_list: - recipe[java::default] - recipe[kafka::default] - excludes: - - centos-6.5 - - debian-7.4 - - fedora-20 + includes: + - ubuntu-14.04 attributes: kafka: init_style: 'upstart' @@ -65,6 +63,23 @@ suites: zookeeper: connect: ['localhost:2181'] connection_timeout_ms: 15_000 + - name: init-style-systemd + run_list: + - recipe[java::default] + - recipe[kafka::default] + includes: + - fedora-20 + attributes: + kafka: + init_style: 'systemd' + version: <%= ENV.fetch('KAFKA_VERSION', '0.8.1.1') %> + checksum: <%= ENV.fetch('KAFKA_CHECKSUM', '""') %> + md5_checksum: <%= ENV.fetch('KAFKA_MD5', '""') %> + log: + dirs: ['/mnt/kafka-logs-1', '/mnt/kafka-logs-2'] + zookeeper: + connect: ['localhost:2181'] + connection_timeout_ms: 15_000 - name: source-init-style-upstart run_list: - recipe[java::default] @@ -100,3 +115,21 @@ suites: zookeeper: connect: ['localhost:2181'] connection_timeout_ms: 15_000 + - name: source-init-style-systemd + run_list: + - recipe[java::default] + - recipe[kafka::default] + includes: + - fedora-20 + attributes: + kafka: + install_method: 'source' + init_style: 'systemd' + version: <%= ENV.fetch('KAFKA_VERSION', '0.8.1.1') %> + checksum: <%= ENV.fetch('KAFKA_CHECKSUM', '""') %> + md5_checksum: <%= ENV.fetch('KAFKA_MD5', '""') %> + log: + dirs: ['/mnt/kafka-logs-1', '/mnt/kafka-logs-2'] + zookeeper: + connect: ['localhost:2181'] + connection_timeout_ms: 15_000 diff --git a/libraries/kafka_helpers.rb b/libraries/kafka_helpers.rb index cbfc1506..5a186f02 100644 --- a/libraries/kafka_helpers.rb +++ b/libraries/kafka_helpers.rb @@ -110,6 +110,15 @@ def kafka_init_opts opts[:script_path] = '/etc/init/kafka.conf' opts[:provider] = ::Chef::Provider::Service::Upstart opts[:permissions] = '644' + when :systemd + opts[:env_path] = '/etc/sysconfig/kafka' + opts[:env_template] = 'systemd/kafka.env.erb' + opts[:source] = value_for_platform_family({ + 'default' => 'systemd/default.erb' + }) + opts[:script_path] = '/etc/systemd/system/kafka.service' + opts[:provider] = ::Chef::Provider::Service::Systemd + opts[:permissions] = '644' end end end diff --git a/recipes/_configure.rb b/recipes/_configure.rb index 4579938d..f5267cb4 100644 --- a/recipes/_configure.rb +++ b/recipes/_configure.rb @@ -34,7 +34,7 @@ end template kafka_init_opts[:env_path] do - source 'env.erb' + source kafka_init_opts.fetch(:env_template, 'env.erb') owner 'root' group 'root' mode '644' diff --git a/spec/recipes/configure_spec.rb b/spec/recipes/configure_spec.rb index 7d3eb0ce..e3141956 100644 --- a/spec/recipes/configure_spec.rb +++ b/spec/recipes/configure_spec.rb @@ -1003,7 +1003,7 @@ it 'configures loggers' do expect(chef_run).to have_configured(path).with('log4j.logger.org.IOItec.zkclient.ZkClient').as('INFO') - expect(chef_run).to have_configured(path).with('log4j.logger.kafka.network.RequestChannel$').as('WARN, requestAppender') + expect(chef_run).to have_configured(path).with('log4j.logger.kafka.network.RequestChannel\$').as('WARN, requestAppender') expect(chef_run).to have_configured(path).with('log4j.logger.kafka.request.logger').as('WARN, requestAppender') expect(chef_run).to have_configured(path).with('log4j.logger.kafka.controller').as('INFO, controllerAppender') expect(chef_run).to have_configured(path).with('log4j.logger.state.change.logger').as('INFO, stateChangeAppender') @@ -1052,31 +1052,31 @@ end it 'sets SCALA_VERSION' do - expect(chef_run).to have_configured(env_path).with('export SCALA_VERSION').as('"2.8.0"') + expect(chef_run).to have_configured(env_path).with('(export |)SCALA_VERSION').as('"2.8.0"') end it 'sets JMX_PORT' do - expect(chef_run).to have_configured(env_path).with('export JMX_PORT').as('"9999"') + expect(chef_run).to have_configured(env_path).with('(export |)JMX_PORT').as('"9999"') end it 'sets KAFKA_LOG4J_OPTS' do - expect(chef_run).to have_configured(env_path).with('export KAFKA_LOG4J_OPTS').as('"-Dlog4j.configuration=file:/opt/kafka/config/log4j.properties"') + expect(chef_run).to have_configured(env_path).with('(export |)KAFKA_LOG4J_OPTS').as('"-Dlog4j.configuration=file:/opt/kafka/config/log4j.properties"') end it 'sets KAFKA_HEAP_OPTS' do - expect(chef_run).to have_configured(env_path).with('export KAFKA_HEAP_OPTS').as('"-Xmx1G -Xms1G"') + expect(chef_run).to have_configured(env_path).with('(export |)KAFKA_HEAP_OPTS').as('"-Xmx1G -Xms1G"') end it 'sets KAFKA_GC_LOG_OPTS' do - expect(chef_run).to have_configured(env_path).with('export KAFKA_GC_LOG_OPTS').as('"-Xloggc:/var/log/kafka/kafka-gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps"') + expect(chef_run).to have_configured(env_path).with('(export |)KAFKA_GC_LOG_OPTS').as('"-Xloggc:/var/log/kafka/kafka-gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps"') end it 'sets KAFKA_OPTS' do - expect(chef_run).to have_configured(env_path).with('export KAFKA_OPTS').as('""') + expect(chef_run).to have_configured(env_path).with('(export |)KAFKA_OPTS').as('""') end it 'sets KAFKA_JVM_PERFORMANCE_OPTS' do - expect(chef_run).to have_configured(env_path).with('export KAFKA_JVM_PERFORMANCE_OPTS').as('"-server -XX:+UseCompressedOops -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSScavengeBeforeRemark -XX:+DisableExplicitGC -Djava.awt.headless=true"') + expect(chef_run).to have_configured(env_path).with('(export |)KAFKA_JVM_PERFORMANCE_OPTS').as('"-server -XX:+UseCompressedOops -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSScavengeBeforeRemark -XX:+DisableExplicitGC -Djava.awt.headless=true"') end it 'sets KAFKA_RUN' do @@ -1185,6 +1185,30 @@ end end end + + context 'when init_style is :systemd' do + it_behaves_like 'an init style' do + let :init_style do + 'systemd' + end + + let :init_path do + '/etc/systemd/system/kafka.service' + end + + let :env_path do + '/etc/sysconfig/kafka' + end + + let :script_permissions do + '644' + end + + let :source_template do + 'systemd/default.erb' + end + end + end end context 'kafka service' do diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb index b436dacf..aa0b2204 100644 --- a/spec/support/matchers.rb +++ b/spec/support/matchers.rb @@ -4,7 +4,7 @@ RSpec::Matchers.define :have_configured do |configuration_file| match do |chef_run| - regexp_str = %(^#{Regexp.quote(@attribute)}) + regexp_str = %(^#{@attribute}) regexp_str << %(=#{Regexp.quote(@value)}$) if @value regexp = Regexp.new(regexp_str) @matcher = ChefSpec::Matchers::RenderFileMatcher.new(configuration_file) diff --git a/templates/default/systemd/default.erb b/templates/default/systemd/default.erb new file mode 100644 index 00000000..6f47d08f --- /dev/null +++ b/templates/default/systemd/default.erb @@ -0,0 +1,8 @@ +[Unit] +Description=<%= @daemon_name %> daemon + +[Service] +User=<%= @user %> +EnvironmentFile=/etc/sysconfig/<%= @daemon_name %> +ExecStart=/bin/bash $KAFKA_RUN $KAFKA_ARGS $KAFKA_CONFIG +TimeoutSec=10 diff --git a/templates/default/systemd/kafka.env.erb b/templates/default/systemd/kafka.env.erb new file mode 100644 index 00000000..d4558dc6 --- /dev/null +++ b/templates/default/systemd/kafka.env.erb @@ -0,0 +1,12 @@ +SCALA_VERSION="<%= node[:kafka][:scala_version] %>" +JMX_PORT="<%= @jmx_port %>" + +KAFKA_LOG4J_OPTS="-Dlog4j.configuration=file:<%= node[:kafka][:config_dir] %>/<%= @log4j_config %>" +KAFKA_HEAP_OPTS="<%= node[:kafka][:heap_opts] %>" +KAFKA_GC_LOG_OPTS="-Xloggc:<%= node[:kafka][:log_dir] %>/kafka-gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps" +KAFKA_OPTS="<%= node[:kafka][:generic_opts] %>" +KAFKA_JVM_PERFORMANCE_OPTS="<%= node[:kafka][:jvm_performance_opts] %>" + +KAFKA_RUN="<%= node[:kafka][:install_dir] %>/bin/kafka-run-class.sh" +KAFKA_ARGS="<%= @main_class %>" +KAFKA_CONFIG="<%= node[:kafka][:config_dir] %>/<%= @config %>" diff --git a/test/integration/init-style-systemd/serverspec/localhost/binary_spec.rb b/test/integration/init-style-systemd/serverspec/localhost/binary_spec.rb new file mode 100644 index 00000000..25d25e50 --- /dev/null +++ b/test/integration/init-style-systemd/serverspec/localhost/binary_spec.rb @@ -0,0 +1,8 @@ +# encoding: utf-8 + +require 'spec_helper' +require 'support/install_common' + +describe 'kafka::binary' do + it_behaves_like 'an install method' +end diff --git a/test/integration/init-style-systemd/serverspec/localhost/required_files_spec.rb b/test/integration/init-style-systemd/serverspec/localhost/required_files_spec.rb new file mode 100644 index 00000000..c09bfe10 --- /dev/null +++ b/test/integration/init-style-systemd/serverspec/localhost/required_files_spec.rb @@ -0,0 +1,49 @@ +# encoding: utf-8 + +require 'spec_helper' + +describe 'required files for upstart init style' do + describe 'environment file' do + let :env_file do + file '/etc/sysconfig/kafka' + end + + it 'exists' do + expect(env_file).to be_a_file + end + + it 'is owned by root' do + expect(env_file).to be_owned_by 'root' + end + + it 'belongs to the root group' do + expect(env_file).to be_grouped_into 'root' + end + + it 'has 644 permissions' do + expect(env_file).to be_mode 644 + end + end + + describe 'init configuration' do + let :init_file do + file '/etc/systemd/system/kafka.service' + end + + it 'exists' do + expect(init_file).to be_a_file + end + + it 'is owned by root' do + expect(init_file).to be_owned_by 'root' + end + + it 'belongs to the root group' do + expect(init_file).to be_grouped_into 'root' + end + + it 'has 644 permissions' do + expect(init_file).to be_mode 644 + end + end +end diff --git a/test/integration/init-style-systemd/serverspec/localhost/service_spec.rb b/test/integration/init-style-systemd/serverspec/localhost/service_spec.rb new file mode 100644 index 00000000..8f9c9042 --- /dev/null +++ b/test/integration/init-style-systemd/serverspec/localhost/service_spec.rb @@ -0,0 +1,150 @@ +# encoding: utf-8 + +require 'spec_helper' +require 'support/service_common' + +describe 'service for systemd init style' do + include_context 'service setup' do + let :start_command do + command 'systemctl start kafka.service' + end + + let :stop_command do + command 'systemctl stop kafka.service' + end + + let :status_command do + command 'systemctl status kafka.service' + end + end + + describe 'service kafka start' do + context 'when kafka is not already running' do + before do + backend.run_command 'systemctl stop kafka.service 2> /dev/null || true' + end + + it 'is not already running' do + expect(kafka_service).not_to be_running + end + + it 'does not print anything' do + expect(start_command).to return_stdout '' + end + + it 'exits with status 0' do + expect(start_command).to return_exit_status 0 + end + + it 'actually starts kafka' do + backend.run_command 'systemctl start kafka.service && sleep 3' + + expect(kafka_service).to be_running + end + + it_behaves_like 'a kafka start command' + end + + context 'when kafka is already running' do + before do + backend.run_command 'systemctl start kafka.service 2> /dev/null || true' + end + + it 'is actually already running' do + expect(kafka_service).to be_running + end + + it 'does not print anything' do + expect(start_command).to return_stdout '' + end + + it 'does not start a new process' do + output = backend.run_command('systemctl status kafka.service').stdout.split("\n") + first_pid = output.grep /Main PID/ + output = backend.run_command('systemctl status kafka.service').stdout.split("\n") + new_pid = output.grep /Main PID/ + expect(first_pid).to eq(new_pid) + end + + it_behaves_like 'a kafka start command' + end + end + + describe 'service kafka stop' do + context 'when kafka is running' do + before do + backend.run_command 'systemctl start kafka.service 2> /dev/null || true' + end + + it 'is actaully already running' do + expect(kafka_service).to be_running + end + + it 'prints nothing' do + expect(stop_command).to return_stdout '' + end + + it 'exits with status 0' do + expect(stop_command).to return_exit_status 0 + end + + it 'actually stops kafka' do + backend.run_command 'systemctl stop kafka.service' + + expect(kafka_service).not_to be_running + end + + it_behaves_like 'a kafka stop command' + end + + context 'when kafka is not running' do + before do + backend.run_command 'systemctl stop kafka.service 2> /dev/null || true' + end + + it 'is not running' do + expect(kafka_service).not_to be_running + end + + it 'prints nothing' do + expect(stop_command).to return_stdout '' + end + + it 'exits with status 0' do + expect(stop_command).to return_exit_status 0 + end + + it_behaves_like 'a kafka stop command' + end + end + + describe 'service kafka status' do + context 'when kafka is running' do + before do + backend.run_command 'systemctl restart kafka.service 2> /dev/null || true' + end + + it 'exits with status 0' do + expect(status_command).to return_exit_status 0 + end + + it 'prints a message that kafka is running' do + expect(status_command).to return_stdout /Active: active \(running\)/ + end + end + + context 'when kafka is not running' do + before do + backend.run_command 'systemctl stop kafka.service 2> /dev/null || true' + end + + it 'exits with status 3' do + expect(status_command).to return_exit_status 3 + end + + it 'prints a message that kafka is stopped' do + expect(status_command).to return_stdout /Active: inactive \(dead\)/ + end + end + end +end