Dear all
I'm trying to define users in a server. As there will be more than one, i want to use de auto-loop capabilities of Cfengine to create the users. Howerver, i face an issue when i try to create a first user. I could not give the slist of gropus_secondary as an slist, Cfengine declare that it's a scalar. Here an excerpt of the code
vars: "users[jerome][policy]" string => "present"; "users[jerome][uid]" string => "1001"; "users[jerome][group_primary]" string => "uusmb"; "users[jerome][groups_secondary]" slist => {"academico"};
"l_users" slist => getindices("users");
users: am_policy_hub:: "\((l_users)" policy => "\)(users[\((l_users)][policy])", uid => "\)(users[\((l_users)][uid])", group_primary => "\)(users[\((l_users)][group_primary])", groups_secondary => "\)(users[$(l_users)][groups_secondary])";
That's get me back this error:
… error: Attempted to give a scalar to a non-scalar type groups_secondary => "\((users[\)(l_users)][groups_secondary])",
As \((users[\)(l_users)][groups_secondary]) should get me back an slist, i could not understand where is my error.
Hopes someone could explain me.
Have a great day!
Hi Jerome,
The groups_secondary attribute expects a list type but it got a string.
When you store an slist and dereference it with $(), CFEngine resolves it as a scalar and iterates over the list elements individually.
$(array[key]) always produces a scalar. When the underlying value is an slist, CFEngine iterates — producing one promise evaluation per list element rather than passing the whole list at once.
bundle agent main
{
vars:
"users[jerome][groups_secondary]" slist => { "adm" };
"users[alice][groups_secondary]" slist => { "adm", "daemon" };
"l_users" slist => getindices("users");
reports:
"SCALAR $(l_users): $(users[$(l_users)][groups_secondary])";
}
R: SCALAR alice: adm R: SCALAR alice: daemon R: SCALAR jerome: adm
Note that alice appears twice, once for each element.
If you use that with groups secondary, it will iterate through
Wrapping the scalar in {} makes it syntactically an slist, so CFEngine accepts it. With a single secondary group this appears to work, but with multiple groups, CFEngine iterates and evaluates the promise once per group element. Each iteration calls usermod -G with one group, replacing the previous, so only the last group survives.
bundle agent main
{
methods:
"setup" usebundle => setup_groups;
"create" usebundle => create_users;
"verify" usebundle => verify_users;
}
bundle agent setup_groups
{
commands:
"/usr/sbin/groupadd -f testuser";
}
bundle agent create_users
{
vars:
"users[testuser][policy]" string => "present";
"users[testuser][uid]" string => "1501";
"users[testuser][group_primary]" string => "testuser";
"users[testuser][groups_secondary]" slist => { "adm", "daemon" };
"l_users" slist => getindices("users");
users:
"$(l_users)"
policy => "$(users[$(l_users)][policy])",
uid => "$(users[$(l_users)][uid])",
group_primary => "$(users[$(l_users)][group_primary])",
groups_secondary => { "$(users[$(l_users)][groups_secondary])" };
}
bundle agent verify_users
{
commands:
"/bin/grep testuser /etc/group";
}
# cf-agent -b main -K -I -f /tmp/cfengine3-2OdfsN
info: Using command line specified bundlesequence
info: Executing 'no timeout' ... '/usr/sbin/groupadd -f testuser'
info: Completed execution of '/usr/sbin/groupadd -f testuser'
info: Created user 'testuser'
info: Modified user 'testuser'
info: Executing 'no timeout' ... '/bin/grep testuser /etc/group'
notice: Q: ".../bin/grep testu": daemon:x:1:testuser
Q: ".../bin/grep testu": testuser:x:1000:
info: Last 2 quoted lines were generated by promiser '/bin/grep testuser /etc/group'
info: Completed execution of '/bin/grep testuser /etc/group'
Note the Created + Modified — that is two separate calls. The user ends up in only the last group.
@(...)getvalues()
getvalues() returns and slist, this shows dynamically pulling a list and still just One Created call (no modification), both groups applied.
bundle agent main
{
methods:
"setup" usebundle => setup_groups;
"create" usebundle => create_users;
"verify" usebundle => verify_users;
}
bundle agent setup_groups
{
commands:
"/usr/sbin/groupadd -f testuser";
}
bundle agent create_users
{
vars:
"users[testuser][policy]" string => "present";
"users[testuser][uid]" string => "1501";
"users[testuser][group_primary]" string => "testuser";
"users[testuser][groups_secondary]" slist => { "adm", "daemon" };
"l_users" slist => getindices("users");
users:
"$(l_users)"
policy => "$(users[$(l_users)][policy])",
uid => "$(users[$(l_users)][uid])",
group_primary => "$(users[$(l_users)][group_primary])",
groups_secondary => getvalues("users[$(l_users)][groups_secondary]");
}
bundle agent verify_users
{
commands:
"/bin/grep testuser /etc/group";
}
# cf-agent -b main -K -I -f /tmp/cfengine3-UgVVZb
info: Using command line specified bundlesequence
info: Executing 'no timeout' ... '/usr/sbin/groupadd -f testuser'
info: Completed execution of '/usr/sbin/groupadd -f testuser'
info: Created user 'testuser'
info: Executing 'no timeout' ... '/bin/grep testuser /etc/group'
notice: Q: ".../bin/grep testu": daemon:x:1:testuser
Q: ".../bin/grep testu": adm:x:4:testuser
Q: ".../bin/grep testu": testuser:x:1000:
info: Last 3 quoted lines were generated by promiser '/bin/grep testuser /etc/group'
info: Completed execution of '/bin/grep testuser /etc/group'
Hi Jerome,
Sending again …. the whole lists example was missing last time. …
The groups_secondary attribute expects a list type but it got a string.
When you store an slist and dereference it with $(), CFEngine resolves it as a scalar and iterates over the list elements individually.
$(array[key]) always produces a scalar. When the underlying value is an slist, CFEngine iterates — producing one promise evaluation per list element rather than passing the whole list at once.
bundle agent main
{
vars:
"users[jerome][groups_secondary]" slist => { "adm" };
"users[alice][groups_secondary]" slist => { "adm", "daemon" };
"l_users" slist => getindices("users");
reports:
"SCALAR $(l_users): $(users[$(l_users)][groups_secondary])";
}
R: SCALAR alice: adm R: SCALAR alice: daemon R: SCALAR jerome: adm
Note that alice appears twice, once for each element.
If you use that with groups secondary, it will iterate through
Wrapping the scalar in {} makes it syntactically an slist, so CFEngine accepts it. With a single secondary group this appears to work, but with multiple groups, CFEngine iterates and evaluates the promise once per group element. Each iteration calls usermod -G with one group, replacing the previous, so only the last group survives.
bundle agent main
{
methods:
"setup" usebundle => setup_groups;
"create" usebundle => create_users;
"verify" usebundle => verify_users;
}
bundle agent setup_groups
{
commands:
"/usr/sbin/groupadd -f testuser";
}
bundle agent create_users
{
vars:
"users[testuser][policy]" string => "present";
"users[testuser][uid]" string => "1501";
"users[testuser][group_primary]" string => "testuser";
"users[testuser][groups_secondary]" slist => { "adm", "daemon" };
"l_users" slist => getindices("users");
users:
"$(l_users)"
policy => "$(users[$(l_users)][policy])",
uid => "$(users[$(l_users)][uid])",
group_primary => "$(users[$(l_users)][group_primary])",
groups_secondary => { "$(users[$(l_users)][groups_secondary])" };
}
bundle agent verify_users
{
commands:
"/bin/grep testuser /etc/group";
}
# cf-agent -b main -K -I -f /tmp/cfengine3-2OdfsN
info: Using command line specified bundlesequence
info: Executing 'no timeout' ... '/usr/sbin/groupadd -f testuser'
info: Completed execution of '/usr/sbin/groupadd -f testuser'
info: Created user 'testuser'
info: Modified user 'testuser'
info: Executing 'no timeout' ... '/bin/grep testuser /etc/group'
notice: Q: ".../bin/grep testu": daemon:x:1:testuser
Q: ".../bin/grep testu": testuser:x:1000:
info: Last 2 quoted lines were generated by promiser '/bin/grep testuser /etc/group'
info: Completed execution of '/bin/grep testuser /etc/group'
Note the Created + Modified — that is two separate calls. The user ends up in only the last group.
@(...)
To pass a whole list use @().
bundle agent main
{
methods:
"setup" usebundle => setup_groups;
"create" usebundle => create_users;
"verify" usebundle => verify_users;
}
bundle agent setup_groups
{
commands:
"/usr/sbin/groupadd -f testuser";
}
bundle agent create_users
{
vars:
"users[testuser][policy]" string => "present";
"users[testuser][uid]" string => "1501";
"users[testuser][group_primary]" string => "testuser";
"users[testuser][groups_secondary]" slist => { "adm", "daemon" };
"l_users" slist => getindices("users");
users:
"$(l_users)"
policy => "$(users[$(l_users)][policy])",
uid => "$(users[$(l_users)][uid])",
group_primary => "$(users[$(l_users)][group_primary])",
groups_secondary => { "@(users[$(l_users)][groups_secondary])" };
}
bundle agent verify_users
{
commands:
"/bin/grep testuser /etc/group";
}
# cf-agent -b main -K -I -f /tmp/cfengine3-PMNpv7
info: Using command line specified bundlesequence
info: Executing 'no timeout' ... '/usr/sbin/groupadd -f testuser'
info: Completed execution of '/usr/sbin/groupadd -f testuser'
info: Created user 'testuser'
info: Executing 'no timeout' ... '/bin/grep testuser /etc/group'
notice: Q: ".../bin/grep testu": daemon:x:1:testuser
Q: ".../bin/grep testu": adm:x:4:testuser
Q: ".../bin/grep testu": testuser:x:1000:
info: Last 3 quoted lines were generated by promiser '/bin/grep testuser /etc/group'
info: Completed execution of '/bin/grep testuser /etc/group'
getvalues()
getvalues() returns and slist, this shows dynamically pulling a list and still just One Created call (no modification), both groups applied.
bundle agent main
{
methods:
"setup" usebundle => setup_groups;
"create" usebundle => create_users;
"verify" usebundle => verify_users;
}
bundle agent setup_groups
{
commands:
"/usr/sbin/groupadd -f testuser";
}
bundle agent create_users
{
vars:
"users[testuser][policy]" string => "present";
"users[testuser][uid]" string => "1501";
"users[testuser][group_primary]" string => "testuser";
"users[testuser][groups_secondary]" slist => { "adm", "daemon" };
"l_users" slist => getindices("users");
users:
"$(l_users)"
policy => "$(users[$(l_users)][policy])",
uid => "$(users[$(l_users)][uid])",
group_primary => "$(users[$(l_users)][group_primary])",
groups_secondary => getvalues("users[$(l_users)][groups_secondary]");
}
bundle agent verify_users
{
commands:
"/bin/grep testuser /etc/group";
}
Hi Jerome,
Sending again …. the whole lists example was missing last time. …
The
groups_secondaryattribute expects a list type but it got a string.When you store an
slistand dereference it with$(), CFEngine resolves it as a scalar and iterates over the list elements individually.
Dear Nick
First of all thank's a lot for your didactic answer. I'm a bit new in this stuff. But i could not find how to get back a slist to the secondary_groups value.
I learn a lot with this code !
Regards!
-- -- Jérôme La musique, c'est du bruit qui pense. (Victor Hugo)