Cookbook : monit_wrapper

monit_wrapper

Build Status

This cookbook simplifies setting up services using Monit.

Examples

Custom Monit configuration template

Create a configuration template in your cookbook my_cookbook/templates/default/monit/my_service.conf.erb:

check process <%= @service_name %>
  matching '<%= @cmd_line_pattern %>'
  every 1 cycles
  start program "/bin/bash -c 'exec <%= @cmd_line %>'"
    as uid <%= @user %> as gid <%= @user %>
  stop program "/usr/bin/pkill -u <%= @user %> -f '<%= @cmd_line_pattern %>'"
    as uid <%= @user %> as gid <%= @user %>

In my_cookbook/recipes/default.rb:


my_service_name = '...'
command_line = '/usr/local/bin/my_service_executable --port 3456'

monit_wrapper_monitor my_service_name do
  template_cookbook 'my_cookbook'
  template_source 'monit/my_service.conf.erb'
  variables cmd_line: command_line,
            cmd_line_pattern: command_line,
            user: user
end

monit_wrapper_notify_if_not_running monit_service_name

monit_wrapper_service my_service_name do
  subscribes :restart, "monit_wrapper_monitor[#{my_service_name}]", :delayed
  subscribes :restart, "monit_wrapper_notify_if_not_running[#{my_service_name}]", :delayed
  subscribes :restart, "package[#{my_service_name}]", :delayed
end

Launching and monitoring a process with an existing init

If you have a service with an existing /etc/init.d script, you can use this cookbook to create a Monit configuration file to monitor that service. This makes use of the default Monit configuration template this cookbook provides.

my_sevice_name = 'my-service'

monit_wrapper_monitor my_service_name do
  action :create
  pattern '...'
end

monit_wrapper_notify_if_not_running my_service_name do

monit_wrapper_service service_name do
  subscribes :restart, "package[#{service_name}]", :delayed
  subscribes :restart, "monit_wrapper_monitor[#{service_name}]", :delayed
  subscribes :restart, "monit_wrapper_notify_if_not_running[#{service_name}]",
             :delayed
end

License

Apache License 2.0

https://www.apache.org/licenses/LICENSE-2.0

Cookbook Documentation

Recipes Summary

Lightweight Resources

monit_wrapper_monitor

    Actions

      Action Description
      create Creates a Monit service configuration file
      delete Deletes a Monit service configuration file

    Attributes

      Attribute Description
      pid_file Specifies the pid_file parameter of our default Monit service configuration template.
      pattern Specifies the pattern parameter (the command line regular expression) of our default Monit service configuration template.
      java_class If this is specified, the command line regular expression is constructed automatically that matches Java processes running the given class.
      variables Additional variables to be used with the Monit service configuration template.
      template_source The source file of the Monit configuration template. A default template is provided by this cookbook.
      template_cookbook The cookbook to look for the Monit configuration template in.
      wait_for_host_port Wait for the given host:port combination to become available over TCP before proceeding with Monit service configuration file creation.

monit_wrapper_service

    Actions

      Action Description
      start Starts the given Monit service.
      stop Stops the given Monit service.
      restart Restarts the given Monit service.
      nothing Does not do anything.

    Attributes

      Attribute Description
      fallback_to_regular_service If this is specified and the service is not registered with Monit, it will be started as a regular service using the standard "service" resource.
      wait_for_host_port A host:port combination of another service. If this is specified, we wait for this the given host to start listening on the given TCP port before attempting to start or restart the service. This could be used for handling dependencies on remote services.

monit_wrapper_reload_and_wait

    Actions

      Action Description
      reload_and_wait Reloads Monit configuration and waits for Monit to start showing the given service in the list of services it knows about. This is invoked by the monit_wrapper_monitor LWRP after creating a new monit service configuration file.

monit_wrapper_notify_if_not_running

    Actions

      Action Description
      run Notifies other resources that subscribe to this resource if the specified Monit service is not running.

Providers

monit_wrapper_monitor

    Actions Summary

    • create Creates a configuration file for a new Monit-monitored service.
    • delete

monit_wrapper_service

    Actions Summary

    • start Start the given Monit service.
    • stop Stop the given Monit service.
    • restart Restart the given Monit service.

monit_wrapper_reload_and_wait

    Actions Summary

    • reload_and_wait Reload Monit configuration and wait for it to become aware of the given service.

monit_wrapper_notify_if_not_running

    Actions Summary

    • run Notifies subscribing resources if the given Monit service is not running.

Recipe Details

monit_wrapper::default



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'recipes/default.rb', line 1

# Copyright 2015 ClearStory Data, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


if platform_family?('rhel') && node['monit']['install_method'] != 'source'
  Chef::Log.warn(
    "Setting node['monit']['install_method'] to 'source' on a RedHat-based system because " \
    "the monit_wrapper cookbook requires a Monit version of 5.2 or later to utilize the " \
    "'matching' feature. Please note that this won't help if the monit-ng default recipe " \
    "is included in the run list before the monit_wrapper recipe."
  )
  node.override['monit']['install_method'] = 'source'
  node.default['monit']['executable'] = '/usr/local/bin/monit'
else
  node.default['monit']['executable'] = '/usr/bin/monit'
end

include_recipe 'monit-ng'

# Ensure monit daemon is running. This may not happen on its own on Docker. We are not using the
# "service" resource, because service[monit] is also defined in monit-ng, and we do not want to
# interfere with that resource's execution here.
ruby_block 'monit_wrapper_start_monit_service' do
  block { ensure_monit_daemon_is_running }
end

chef_gem 'waitutil'

template '/usr/local/bin/start_stop_service_from_monit.sh' do
  source 'start_stop_service_from_monit.sh.erb'
  owner 'root'
  group 'root'
  mode '0744'
  variables timeout_sec: node['monit_wrapper']['start_stop_timeout_sec']
end

template '/usr/local/bin/monit_service_ctl.sh' do
  source 'monit_service_ctl.sh.erb'
  owner 'root'
  group 'root'
  mode '0755'
  variables monit_executable: node['monit']['executable']
end

# We use this directory for lock files to ensure that no more than one process is trying to
# start/stop a particular service from start_stop_service_from_monit.sh at any given moment.
directory '/var/monit' do
  owner 'root'
  group 'root'
  mode '0777'
end

Action Details

create  (Chef::Provider::MonitWrapperMonitor)

Creates a configuration file for a new Monit-monitored service.



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'providers/monitor.rb', line 28

action :create do  # ~FC017
  # We disable FC017 (LWRP does not notify when updated) because we are using
  # notifying_action_wrapper that notifies all subscribers when the template is regenerated.

  notifying_action_wrapper(
    allow_updates_from: "template[#{service_conf_path}]",
    verbose: true
  ) do
    wait_for_host_port(new_resource.wait_for_host_port)

    variables = (new_resource.variables || {}).to_hash.clone

    variables[:service_name] = new_resource.name
    variables[:pid_file] = new_resource.pid_file if new_resource.pid_file
    variables[:pattern] = new_resource.pattern if new_resource.pattern
    if new_resource.java_class
      variables[:pattern] = "^[^ ]*java .* #{new_resource.java_class}($| .*)"
    end
    if [new_resource.pid_file,
        new_resource.pattern,
        new_resource.java_class].compact.size > 1
      raise 'No more than one of pid file, pattern, or Java class can be specified. ' +
            "Variables: #{variables.inspect}"
    end

    Chef::Log.info(
      "Creating Monit configuration file #{service_conf_path} " \
      "for service #{new_resource.name}"
    )

    file deprecated_service_conf_path do
      action :delete
    end

    template service_conf_path do
      owner 'root'
      group 'root'
      mode  '0644'
      source new_resource.template_source || 'service_wrapper.conf.erb'
      cookbook new_resource.template_cookbook || 'monit_wrapper'
      variables variables
      action :create
    end

    Chef::Log.info("Reloading Monit configuration and waiting for service #{new_resource.name}")
    monit_wrapper_reload_and_wait new_resource.name
  end
end

delete  (Chef::Provider::MonitWrapperMonitor)



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'providers/monitor.rb', line 77

action :delete do
  if ::File.exists?(service_conf_path)
    file service_conf_path do
      action :delete
    end

    file deprecated_service_conf_path do
      action :delete
    end

    bash "monit-reload-after-removing-#{new_resource.name}" do
      code 'monit reload'
    end

    new_resource.updated_by_last_action(true)
  end
end

start  (Chef::Provider::MonitWrapperService)

Start the given Monit service.



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'providers/service.rb', line 19

action :start do  # ~FC017
  # We disable FC017 because notifying_action_wrapper takes care of notifications.
  service_name = new_resource.name
  if monit_service_running?(service_name)
    Chef::Log.info("Service #{service_name} is already running, skipping start action")
  elsif monit_service_registered?(service_name)
    wait_for_host_port(new_resource.wait_for_host_port)
    unless monit_service_running?(service_name, verbose: true)
      start_monit_service(service_name)
    end
  elsif new_resource.fallback_to_regular_service
    wait_for_host_port(new_resource.wait_for_host_port)
    Chef::Log.info(
      "No Monit service #{service_name} registered, failling back to starting a regular service")
    service service_name do
      action :start
    end
  else
    raise "Monit does not know about #{service_name} and fallback_to_regular_service is disabled"
  end
end

stop  (Chef::Provider::MonitWrapperService)

Stop the given Monit service.



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'providers/service.rb', line 42

action :stop do  # ~FC017
  # We disable FC017 because notifying_action_wrapper takes care of notifications.
  service_name = new_resource.name
  if monit_service_registered?(service_name)
    stop_monit_service(service_name)
  elsif new_resource.fallback_to_regular_service
    Chef::Log.info(
      "No Monit service #{service_name} registered, failling back to stopping a regular service")
    service service_name do
      action :stop
    end
  else
    raise "Monit does not know about #{service_name} and fallback_to_regular_service is disabled"
  end
end

restart  (Chef::Provider::MonitWrapperService)

Restart the given Monit service.



59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'providers/service.rb', line 59

action :restart do  # ~FC017
  # We disable FC017 because notifying_action_wrapper takes care of notifications.
  if monit_service_running?(new_resource.name, verbose: true)
    command = 'restart'
  else
    command = 'start'
  end
  wait_for_host_port(new_resource.wait_for_host_port)
  bash "monit-#{command}-#{new_resource.name}" do
    user 'root'
    code "#{node['monit']['executable']} #{command} #{new_resource.name}"
    action :run
  end
end

reload_and_wait  (Chef::Provider::MonitWrapperReloadAndWait)

Reload Monit configuration and wait for it to become aware of the given service.



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'providers/reload_and_wait.rb', line 18

action :reload_and_wait do
  service_name = new_resource.name
  script 'monit-reload' do
    interpreter 'bash'
    user 'root'
    code "#{node['monit']['executable']} reload"
  end

  # The Monit daemon suddenly stops after the above reload action in some cases. We are working
  # around that bug by restarting the Monit daemon here.
  ruby_block "ensure-monit-is-running-after-reloading-for-#{service_name}" do
    block { ensure_monit_daemon_is_running }
  end

  ruby_block "wait-for-monit-reload-#{new_resource.name}" do
    block { wait_for_monit_service_to_exist(new_resource.name) }
  end

  new_resource.updated_by_last_action(true)
end

run  (Chef::Provider::MonitWrapperNotifyIfNotRunning)

Notifies subscribing resources if the given Monit service is not running.



16
17
18
19
20
21
22
23
# File 'providers/notify_if_not_running.rb', line 16

action :run do
  if monit_service_running?(new_resource.name, verbose: true)
    Chef::Log.info("Service #{new_resource.name} is running, not sending notification")
  else
    Chef::Log.info("Service #{new_resource.name} is not running, sending notification")
    new_resource.updated_by_last_action(true)
  end
end