Skip to content

Howto write a service bundle

Bas van der Vlies edited this page May 23, 2022 · 7 revisions

How to write a service bundle

Here are some guidelines to write service bundle, eg: ntp.cf. We use this as example to explain the library.

General rules

Here are some general writing rules for SURF services:

  • Always define an bundle common <service_name>
  • Always define an bundle agent <service_name>_autorun() with meta tags:
    • "tags" slist => { "service_<service_name>", "template_<service_name>" };
  • Class names that are defined within the bundle must start with <service_name>_
  • Bundles must start with <service_name>_
  • Always start a bundle with the promises type if used:
    • vars
    • classes
  • packages are installed with the new package external module. This is the default behavior.
  • Inline documentation at the end of the file and must be present

ntp.cf explained

In the following section we will explain the ntp.cf setup. Keep in mind the General rules section.

bundle common ntp

This bundle must be present.

bundle common ntp {
    vars:
        any::
            "template_2_destination" data => parsejson('
                    {
                        "ntp.mustache" : "$(config_file)",
                        "ntp_default.mustache" : "$(ntp_default)",
                        "ntpdate_default.mustache" : "$(ntpdate_default)",
                    }
                ');

            "mode"              string => "0644";
            "group"             string => "root";
            "owner"             string => "root";
            "restart"           string => "$(paths.path[service]) $(service_name) restart";

        centos::
            "daemon_name"       string => "ntpd";
            "config_file"       string => "/etc/ntp.conf";
            "ntp_default"       string => "/etc/sysconfig/ntpd";
            "ntpdate_default"   string => "/etc/sysconfig/ntpdate";
            "packages"  data  => parsejson('
                    {
                        "install": {
                            "ntp" : "",
                            "ntpdate" : ""
                        }
                    }
                ');
            "service_name"      string => "ntpd";

        debian::
            "daemon_name"       string => "ntpd";
            "config_file"       string => "/etc/ntp.conf";
            "ntp_default"       string => "/etc/default/ntp";
            "ntpdate_default"   string => "/etc/default/ntpdate";
            "packages"  data  => parsejson('
                    {
                        "install": {
                            "ntp" : "",  
                            "ntpdate" : ""
                        }
                    }
                ');
            "service_name"      string => "ntp";
}

In the common bundle we define the template to be used and the destination of the generated file(s). Variables will be defined that you can use in the other ntp bundles. You can also easily see the difference between the oses like centos vs debian. This bundle is always defined.

bundle agent ntp_autorun

This bundle must be present.

bundle agent ntp_autorun()
{
    meta:
        "tags" slist => { "service_ntp", "template_ntp" };

    methods:
        any::
            "" usebundle => ntp_install();
            "" usebundle => ntp_config();
            "" usebundle => ntp_daemons_check();

Usually you want to install, config and check the daemons if they are defined. Most of the services work this way, there are exceptions and you may write as many bundles that you need to full fill the task. There must be a autorun bundle and no parameters are allowed. If an service needs a data directory to copy from it can use the following bundles:

  • scl_service_copy_file
"ssh": {
    "copy_files":  [
                {
                    "dest": "$(ssh.config_dir)/shosts.equiv",
                    "source": "data/ssh/lisa/shosts.equiv",
                    "mog": [ "0644", "root", "root" ]
                }
    ]
}
  • scl_service_copy_dirs
    "copy_dirs":  [
        {
            "dest": "$(scl.node_exporter[dir])",
            "exclude_dirs": [ ".git", ".svn" ],
            "purge": "true",
            "run_bundle": "node_exporter_restart",
            "source": "data/prometheus_exporters/node_exporter-0.15.2"
        }
    ]

bundle agent ntp_install

Here the general rule of package installation will be shown:

bundle agent ntp_install()
{
    methods:
        "" usebundle => scl_service_packages("ntp", "@(ntp.packages)");
}

Notice the rule The new package method is used. This is available for a lot of operating systems. If it is not supported make an exception protected by an class statement. The scl_service_packages install the default packages defined in the common bundle. This can be overridden in def.cf/def.json.

ntp_config

Here the configuration files will be generated and checked if they are up to date and have the right mode. If a configuration file has been changed we can define an action as to restart the daemon.

bundle agent ntp_config()
{
    classes:
        "NTP_RESTART" or => {
            canonify("scl$(ntp.ntp_default)"),
            canonify("scl$(ntp.config_file)")
         };

    files:
        any::
            "$(ntp.config_file)"
                perms => mog("$(ntp.mode)", "$(ntp.owner)", "$(ntp.group)");
            "$(ntp.ntp_default)"
                perms => mog("$(ntp.mode)", "$(ntp.owner)", "$(ntp.group)");
            "$(ntp.ntpdate_default)"
                perms => mog("$(ntp.mode)", "$(ntp.owner)", "$(ntp.group)");

    methods:
        any::
            "" usebundle => scl_mustache_autorun("ntp");

        NTP_RESTART::
            "" usebundle => ntp_daemons_restart();
}

bundle agent ntp_daemons_check

As the name suggest it will check if the daemon is running, if not start and report.

bundle agent ntp_daemons_check()
{
    processes:
       any::
            "$(ntp.daemon_name)"
                comment         => "Check if ntpd is running",
                process_count   => check_range("$(ntp.daemon_name)", "1", "1"),
                process_select  => scl_select_parent_process("1");

    methods:
        any::
            "" usebundle => ntp_daemons_restart(),
                if => canonify("$(ntp.daemon_name)_out_of_range");
}