condition: add a condition that matches against the machine tags
This commit is contained in:
@@ -2058,6 +2058,25 @@
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>ConditionMachineTag=</varname></term>
|
||||
|
||||
<listitem><para><varname>ConditionMachineTag=</varname> may be used to match against the tags
|
||||
assigned to the local machine. Machine tags are short labels that classify and group machines for
|
||||
management purposes; they are configured in the <varname>TAGS=</varname> field of
|
||||
<citerefentry><refentrytitle>machine-info</refentrytitle><manvolnum>5</manvolnum></citerefentry> and
|
||||
may be queried and changed with the <command>tags</command> command of
|
||||
<citerefentry><refentrytitle>hostnamectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>. The
|
||||
argument is a single tag pattern, which is compared against each of the configured tags using
|
||||
shell-style globbing (<literal>*</literal>, <literal>?</literal>, <literal>[]</literal>). The
|
||||
condition is satisfied if at least one of the configured tags matches the pattern. The test may be
|
||||
negated by prepending an exclamation mark, in which case it is satisfied if none of the configured
|
||||
tags matches.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v261"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>ConditionMemoryPressure=</varname></term>
|
||||
<term><varname>ConditionCPUPressure=</varname></term>
|
||||
@@ -2126,6 +2145,7 @@
|
||||
<term><varname>AssertCPUs=</varname></term>
|
||||
<term><varname>AssertCPUFeature=</varname></term>
|
||||
<term><varname>AssertOSRelease=</varname></term>
|
||||
<term><varname>AssertMachineTag=</varname></term>
|
||||
<term><varname>AssertMemoryPressure=</varname></term>
|
||||
<term><varname>AssertCPUPressure=</varname></term>
|
||||
<term><varname>AssertIOPressure=</varname></term>
|
||||
|
||||
@@ -398,6 +398,7 @@ Unit.ConditionUser, config_parse_unit_condition_string
|
||||
Unit.ConditionGroup, config_parse_unit_condition_string, CONDITION_GROUP, offsetof(Unit, conditions)
|
||||
Unit.ConditionControlGroupController, config_parse_unit_condition_string, CONDITION_CONTROL_GROUP_CONTROLLER, offsetof(Unit, conditions)
|
||||
Unit.ConditionOSRelease, config_parse_unit_condition_string, CONDITION_OS_RELEASE, offsetof(Unit, conditions)
|
||||
Unit.ConditionMachineTag, config_parse_unit_condition_string, CONDITION_MACHINE_TAG, offsetof(Unit, conditions)
|
||||
Unit.ConditionMemoryPressure, config_parse_unit_condition_string, CONDITION_MEMORY_PRESSURE, offsetof(Unit, conditions)
|
||||
Unit.ConditionCPUPressure, config_parse_unit_condition_string, CONDITION_CPU_PRESSURE, offsetof(Unit, conditions)
|
||||
Unit.ConditionIOPressure, config_parse_unit_condition_string, CONDITION_IO_PRESSURE, offsetof(Unit, conditions)
|
||||
@@ -434,6 +435,7 @@ Unit.AssertUser, config_parse_unit_condition_string
|
||||
Unit.AssertGroup, config_parse_unit_condition_string, CONDITION_GROUP, offsetof(Unit, asserts)
|
||||
Unit.AssertControlGroupController, config_parse_unit_condition_string, CONDITION_CONTROL_GROUP_CONTROLLER, offsetof(Unit, asserts)
|
||||
Unit.AssertOSRelease, config_parse_unit_condition_string, CONDITION_OS_RELEASE, offsetof(Unit, asserts)
|
||||
Unit.AssertMachineTag, config_parse_unit_condition_string, CONDITION_MACHINE_TAG, offsetof(Unit, asserts)
|
||||
Unit.AssertMemoryPressure, config_parse_unit_condition_string, CONDITION_MEMORY_PRESSURE, offsetof(Unit, asserts)
|
||||
Unit.AssertCPUPressure, config_parse_unit_condition_string, CONDITION_CPU_PRESSURE, offsetof(Unit, asserts)
|
||||
Unit.AssertIOPressure, config_parse_unit_condition_string, CONDITION_IO_PRESSURE, offsetof(Unit, asserts)
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "glob-util.h"
|
||||
#include "hmac.h"
|
||||
#include "hostname-setup.h"
|
||||
#include "hostname-util.h"
|
||||
#include "id128-util.h"
|
||||
#include "ima-util.h"
|
||||
#include "initrd-util.h"
|
||||
@@ -316,6 +317,37 @@ static int condition_test_osrelease(Condition *c, char **env) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static int condition_test_machine_tag(Condition *c, char **env) {
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
assert(c->parameter);
|
||||
assert(c->type == CONDITION_MACHINE_TAG);
|
||||
|
||||
_cleanup_free_ char *tags = NULL;
|
||||
r = parse_env_file(
|
||||
/* f= */ NULL, etc_machine_info(),
|
||||
"TAGS", &tags);
|
||||
if (r < 0 && r != -ENOENT) {
|
||||
log_debug_errno(r, "Failed to read /etc/machine-info, ignoring: %m");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Silently ignore invalid tags, matching the Tags D-Bus property */
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
r = machine_tags_from_string(tags, /* graceful= */ true, &l);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to parse machine tags '%s', ignoring: %m", tags);
|
||||
return false;
|
||||
}
|
||||
|
||||
STRV_FOREACH(i, l)
|
||||
if (fnmatch(c->parameter, *i, /* flags= */ 0) == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int condition_test_memory(Condition *c, char **env) {
|
||||
CompareOperator operator;
|
||||
uint64_t m, k;
|
||||
@@ -1344,6 +1376,7 @@ int condition_test(Condition *c, char **env) {
|
||||
[CONDITION_ENVIRONMENT] = condition_test_environment,
|
||||
[CONDITION_CPU_FEATURE] = condition_test_cpufeature,
|
||||
[CONDITION_OS_RELEASE] = condition_test_osrelease,
|
||||
[CONDITION_MACHINE_TAG] = condition_test_machine_tag,
|
||||
[CONDITION_MEMORY_PRESSURE] = condition_test_psi,
|
||||
[CONDITION_CPU_PRESSURE] = condition_test_psi,
|
||||
[CONDITION_IO_PRESSURE] = condition_test_psi,
|
||||
@@ -1471,6 +1504,7 @@ static const char* const _condition_type_table[_CONDITION_TYPE_MAX] = {
|
||||
[CONDITION_ENVIRONMENT] = "ConditionEnvironment",
|
||||
[CONDITION_CPU_FEATURE] = "ConditionCPUFeature",
|
||||
[CONDITION_OS_RELEASE] = "ConditionOSRelease",
|
||||
[CONDITION_MACHINE_TAG] = "ConditionMachineTag",
|
||||
[CONDITION_MEMORY_PRESSURE] = "ConditionMemoryPressure",
|
||||
[CONDITION_CPU_PRESSURE] = "ConditionCPUPressure",
|
||||
[CONDITION_IO_PRESSURE] = "ConditionIOPressure",
|
||||
@@ -1528,6 +1562,7 @@ static const char* const _assert_type_table[_CONDITION_TYPE_MAX] = {
|
||||
[CONDITION_ENVIRONMENT] = "AssertEnvironment",
|
||||
[CONDITION_CPU_FEATURE] = "AssertCPUFeature",
|
||||
[CONDITION_OS_RELEASE] = "AssertOSRelease",
|
||||
[CONDITION_MACHINE_TAG] = "AssertMachineTag",
|
||||
[CONDITION_MEMORY_PRESSURE] = "AssertMemoryPressure",
|
||||
[CONDITION_CPU_PRESSURE] = "AssertCPUPressure",
|
||||
[CONDITION_IO_PRESSURE] = "AssertIOPressure",
|
||||
|
||||
@@ -21,6 +21,7 @@ typedef enum ConditionType {
|
||||
CONDITION_ENVIRONMENT,
|
||||
CONDITION_CPU_FEATURE,
|
||||
CONDITION_OS_RELEASE,
|
||||
CONDITION_MACHINE_TAG,
|
||||
CONDITION_MEMORY_PRESSURE,
|
||||
CONDITION_CPU_PRESSURE,
|
||||
CONDITION_IO_PRESSURE,
|
||||
|
||||
@@ -1414,6 +1414,69 @@ TEST(condition_test_os_release) {
|
||||
condition_free(condition);
|
||||
}
|
||||
|
||||
TEST(condition_test_machine_tag) {
|
||||
Condition *condition;
|
||||
|
||||
/* etc_machine_info() caches the path on first use, so redirect it before anything reads it and
|
||||
* rewrite the same file (truncating) for each scenario rather than switching paths. */
|
||||
_cleanup_free_ char *saved = NULL;
|
||||
ASSERT_OK(free_and_strdup(&saved, getenv("SYSTEMD_ETC_MACHINE_INFO")));
|
||||
|
||||
_cleanup_(rm_rf_physical_and_freep) char *d = NULL;
|
||||
ASSERT_OK(mkdtemp_malloc(NULL, &d));
|
||||
|
||||
_cleanup_free_ char *f = path_join(d, "machine-info");
|
||||
ASSERT_NOT_NULL(f);
|
||||
ASSERT_OK_ERRNO(setenv("SYSTEMD_ETC_MACHINE_INFO", f, /* overwrite= */ true));
|
||||
|
||||
ASSERT_OK(write_string_file(f, "TAGS=\"webserver:frontend:berlin\"\n",
|
||||
WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_TRUNCATE));
|
||||
|
||||
/* Exact match */
|
||||
ASSERT_NOT_NULL((condition = condition_new(CONDITION_MACHINE_TAG, "webserver", /* trigger= */ false, /* negate= */ false)));
|
||||
ASSERT_OK_POSITIVE(condition_test(condition, environ));
|
||||
condition_free(condition);
|
||||
|
||||
/* Glob match */
|
||||
ASSERT_NOT_NULL((condition = condition_new(CONDITION_MACHINE_TAG, "front*", /* trigger= */ false, /* negate= */ false)));
|
||||
ASSERT_OK_POSITIVE(condition_test(condition, environ));
|
||||
condition_free(condition);
|
||||
|
||||
/* No match */
|
||||
ASSERT_NOT_NULL((condition = condition_new(CONDITION_MACHINE_TAG, "database", /* trigger= */ false, /* negate= */ false)));
|
||||
ASSERT_OK_ZERO(condition_test(condition, environ));
|
||||
condition_free(condition);
|
||||
|
||||
/* Negation matches when the tag is absent, ... */
|
||||
ASSERT_NOT_NULL((condition = condition_new(CONDITION_MACHINE_TAG, "database", /* trigger= */ false, /* negate= */ true)));
|
||||
ASSERT_OK_POSITIVE(condition_test(condition, environ));
|
||||
condition_free(condition);
|
||||
|
||||
/* ... and does not match when the tag is present */
|
||||
ASSERT_NOT_NULL((condition = condition_new(CONDITION_MACHINE_TAG, "webserver", /* trigger= */ false, /* negate= */ true)));
|
||||
ASSERT_OK_ZERO(condition_test(condition, environ));
|
||||
condition_free(condition);
|
||||
|
||||
/* Invalid tags in the file are ignored (matching the Tags D-Bus property) */
|
||||
ASSERT_OK(write_string_file(f, "TAGS=\"in valid:good\"\n",
|
||||
WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_TRUNCATE));
|
||||
ASSERT_NOT_NULL((condition = condition_new(CONDITION_MACHINE_TAG, "in valid", /* trigger= */ false, /* negate= */ false)));
|
||||
ASSERT_OK_ZERO(condition_test(condition, environ));
|
||||
condition_free(condition);
|
||||
ASSERT_NOT_NULL((condition = condition_new(CONDITION_MACHINE_TAG, "good", /* trigger= */ false, /* negate= */ false)));
|
||||
ASSERT_OK_POSITIVE(condition_test(condition, environ));
|
||||
condition_free(condition);
|
||||
|
||||
/* No tags configured at all → never matches */
|
||||
ASSERT_OK(write_string_file(f, "PRETTY_HOSTNAME=\"x\"\n",
|
||||
WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_TRUNCATE));
|
||||
ASSERT_NOT_NULL((condition = condition_new(CONDITION_MACHINE_TAG, "webserver", /* trigger= */ false, /* negate= */ false)));
|
||||
ASSERT_OK_ZERO(condition_test(condition, environ));
|
||||
condition_free(condition);
|
||||
|
||||
ASSERT_OK(set_unset_env("SYSTEMD_ETC_MACHINE_INFO", saved, /* overwrite= */ true));
|
||||
}
|
||||
|
||||
TEST(condition_test_psi) {
|
||||
Condition *condition;
|
||||
CGroupMask mask;
|
||||
|
||||
@@ -24,6 +24,7 @@ AssertHost=
|
||||
AssertIOPressure=
|
||||
AssertKernelCommandLine=
|
||||
AssertKernelVersion=
|
||||
AssertMachineTag=
|
||||
AssertMemoryPressure=
|
||||
AssertNeedsUpdate=
|
||||
AssertOSRelease=
|
||||
@@ -72,6 +73,7 @@ ConditionHost=
|
||||
ConditionIOPressure=
|
||||
ConditionKernelCommandLine=
|
||||
ConditionKernelVersion=
|
||||
ConditionMachineTag=
|
||||
ConditionMemoryPressure=
|
||||
ConditionNeedsUpdate=
|
||||
ConditionOSRelease=
|
||||
|
||||
Reference in New Issue
Block a user