21

At my organization we have a number of simple-to-use base AMIs for different services such as ECS and Docker. Since many of our projects involve CloudFormation, we're using cfn-bootstrap, which consists of a couple of scripts and a service which run on boot to install certain packages and do certain configuration management tasks for us.

On startup of a system, an equivalent of the following script must be executed:

#!/bin/bash

# capture stderr only
output="$(cfn-init -s $STACK_NAME -r $RESOURCE_NAME --region $REGION >/dev/null)"

# if it failed, signal to CloudFormation that it failed and include a reason
returncode=$?
if [[ $returncode == 0]]; then
    cfn-signal -e $returncode -r "$output"
    exit $returncode
fi

# otherwise, signal success
cfn-signal -s

I was thinking of running this as a systemd oneshot service which runs After=network.target and WantedBy=multi-user.target.

The only problem is that I'd like my AMI to be flexible and only execute this if a certain file exists. Rather than embedding the above script into the EC2 user data, I can have the user data just define an environment file which defines the variables I need and only run my one-shot service if that environment file exists:

#cloud-init
write_files:
    - path: /etc/sysconfig/cloudformation
      # ...
      content: |
          CFN_STACK_NAME="stack-name"
          CFN_RESOURCE="resource-name"
          CFN_REGION="region"

Is there a way to make systemd only run a service if a given condition is met?

2 Answers 2

27

systemd provides a wide variety of conditions you can test. For instance, you can use ConditionPathExists= to test for the existence of a file.

[Unit]
ConditionPathExists=/etc/sysconfig/cloudformation
3
  • 5
    It's worth noting that this isn't a while condition, but an if, meaning that if the path specified in ConditionPathExists doesn't exist by the time the service starts the rest of the service will just not run. I.e., it does not wait for the path to exist.
    – Mahn
    Jul 12, 2016 at 20:59
  • @Mahn using a systemd timer, it should be possible to repeatedly trigger the service on an interval to overcome that limitation. Feb 15, 2017 at 23:23
  • 2
    @Mahn Take a look at freedesktop.org/software/systemd/man/systemd.path.html#. It can monitor a path and provide activation based on, for example, when a path comes into existence.
    – benf
    Sep 13, 2019 at 22:28
3

I stumbled upon this question looking for ways to start a systemd service using a condition. There are many ways:

ConditionArchitecture=, ConditionVirtualization=, ConditionHost=, ConditionKernelCommandLine=, ConditionSecurity=, ConditionCapability=, ConditionACPower=, ConditionNeedsUpdate=, ConditionFirstBoot=, ConditionPathExists=, ConditionPathExistsGlob=, ConditionPathIsDirectory=, ConditionPathIsSymbolicLink=, ConditionPathIsMountPoint=, ConditionPathIsReadWrite=, ConditionDirectoryNotEmpty=, ConditionFileNotEmpty=, ConditionFileIsExecutable=

I wanted to start the service based on a specific host name.

ConditionHost= may be used to match against the hostname or machine ID of the host. This either takes a hostname string (optionally with shell style globs) which is tested against the locally set hostname as returned by gethostname(2), or a machine ID formatted as string (see machine-id(5)). The test may be negated by prepending an exclamation mark.

More on that here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.