Skip to content

Commit f287922

Browse files
committed
RFC: Ansible INI promise module wrapper
Signed-off-by: Ole Petter <ole.orhagen@northern.tech>
1 parent 9718a0b commit f287922

File tree

4 files changed

+215
-0
lines changed

4 files changed

+215
-0
lines changed

promise-types/ini/README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Ansible INI promise type
2+
3+
## Synopsis
4+
5+
* *Name*: `Ansible INI - Custom Promise Module`
6+
* *Version*: `0.0.1`
7+
* *Description*: Manage TOML configuration files through the Ansible INI module in CFEngine.
8+
9+
## Requirements
10+
11+
* TODO - TBD
12+
13+
## Attributes
14+
15+
See [anible_ini module](https://docs.ansible.com/ansible/latest/collections/community/general/ini_file_module.html).
16+
17+
## Example
18+
19+
```cfengine3
20+
bundle agent main
21+
{
22+
ini:
23+
"/path/to/file.toml"
24+
section => "foo",
25+
option => "fav",
26+
value => "lemonade";
27+
}
28+
```

promise-types/ini/enable.cf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
promise agent ini
2+
# @brief Define ini promise type
3+
{
4+
path => "$(sys.workdir)/inputs/modules/promises/ini.py";
5+
interpreter => "/usr/bin/python3";
6+
}

promise-types/ini/example.cf

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
promise agent ini
2+
# @brief Define ini promise type
3+
{
4+
path => "$(sys.workdir)/inputs/modules/promises/ini.py";
5+
interpreter => "/usr/bin/python3";
6+
}
7+
8+
body common control {
9+
bundlesequence => {"dependencies", "main"};
10+
}
11+
12+
body perms m_rxdirs( mode, rxdirs )
13+
{
14+
mode => "$(mode)";
15+
rxdirs => "$(rxdirs)";
16+
}
17+
18+
bundle agent dependencies {
19+
vars:
20+
21+
"options_str" string => '
22+
{
23+
"url.max_content": 1048576,
24+
"url.verbose": 0
25+
}';
26+
27+
"options" data => parsejson($(options_str));
28+
29+
"url" string => "https://raw.githubusercontent.com/ansible-collections/community.general/9946f758af5fe0fe41f1cf7584d670ca1d6c2d52/plugins/modules/ini_file.py";
30+
31+
"ansible_ini_file" data => url_get($(url), options);
32+
33+
34+
classes:
35+
"got_ini_file" expression => "$(ansible_ini_file[success])";
36+
37+
38+
files:
39+
got_ini_file::
40+
"/tmp/cfe/ini_file.py"
41+
content => "$(ansible_ini_file[content])";
42+
43+
44+
got_ini_file::
45+
"/tmp/cfe/ini_file.py"
46+
perms => m_rxdirs( "0555", "true" );
47+
48+
# TODO - How to make sure that Python is installed
49+
# packages:
50+
# "python"
51+
# policy => "present",
52+
# package_module => generic,
53+
# comment => "Install python";
54+
55+
reports:
56+
57+
got_ini_file::
58+
"$(this.bundle): Successfully retrieved the Ansible INI policy file";
59+
60+
!got_ini_file::
61+
"$(this.bundle): failed to get the Ansible INI policy file $(ansible_ini_file[error_message])";
62+
63+
}
64+
65+
bundle agent main
66+
{
67+
68+
meta:
69+
"tags" slist => { "autorun" };
70+
"bundle_version" string => "0.0.1";
71+
"promise_type" string => "ini";
72+
73+
ini:
74+
"/home/olepor/cfengine/modules/promise-types/ini/test.toml"
75+
module_path => "/tmp/cfe/ini_file.py",
76+
section => "foo",
77+
option => "bar",
78+
value => "baz";
79+
}

promise-types/ini/ini.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
"""The ultimate CFEngine custom promise module"""
2+
3+
import glob
4+
import json
5+
import os
6+
import shutil
7+
import subprocess
8+
import sys
9+
import tempfile
10+
11+
from cfengine import PromiseModule, ValidationError, Result
12+
13+
14+
class AnsiballINIModule(PromiseModule):
15+
def __init__(self):
16+
super().__init__("ansible_ini_promise_module", "0.0.1")
17+
18+
# AnsiballINIModule specific (the path from the policy)
19+
self.add_attribute("module_path", str, default="/tmp/cfe/ini_file.py")
20+
21+
self.add_attribute("path", str, default_to_promiser=True)
22+
self.add_attribute("allow_no_value", bool, default=False)
23+
self.add_attribute("attributes", str)
24+
self.add_attribute("backup", bool, default=False)
25+
self.add_attribute("create", str, default="yes")
26+
self.add_attribute("group", str)
27+
self.add_attribute("mode", str)
28+
self.add_attribute("no_extra_spaces", str, default="yes")
29+
self.add_attribute("option", str)
30+
self.add_attribute("owner", str)
31+
self.add_attribute("section", str, required=True)
32+
# TODO - Add seLinux attrs
33+
# self.add_attribute("selevel", str, default="sO")
34+
# self.add_attribute("serole")
35+
36+
self.add_attribute("state", str, default="present")
37+
self.add_attribute("unsafe_writes", bool, default=False)
38+
self.add_attribute("value", str)
39+
40+
def validate_promise(self, promiser: str, attributes: dict, meta: dict) -> None:
41+
self.log_debug(
42+
"Validating the ansible ini promise: %s %s %s"
43+
% (promiser, attributes, meta)
44+
)
45+
46+
if not meta.get("promise_type"):
47+
raise ValidationError("Promise type not specified")
48+
49+
# Not an Ansible attribute
50+
if not attributes.get("module_path"):
51+
self.log_error("'module_path' is a required promise attribute")
52+
return (result.NOT_KEPT, [])
53+
54+
def evaluate_promise(self, promiser: str, attributes: dict, meta: dict):
55+
self.log_debug(
56+
"Evaluating the ansible ini promise %s, %s, %s"
57+
% (promiser, attributes, meta)
58+
)
59+
60+
# INI module specific - should not be passed on to Ansible
61+
module_path = attributes["module_path"]
62+
del attributes["module_path"]
63+
64+
# NOTE - needed because 'default_to_promiser' is not respected
65+
attributes.setdefault("path", promiser)
66+
67+
proc = subprocess.run(
68+
[
69+
"python",
70+
module_path,
71+
],
72+
input=json.dumps({"ANSIBLE_MODULE_ARGS": attributes}).encode("utf-8"),
73+
stdout=subprocess.PIPE,
74+
stderr=subprocess.PIPE,
75+
)
76+
77+
if not proc:
78+
self.log_error("Failed to run the ansible module")
79+
return (
80+
Result.NOT_KEPT,
81+
[],
82+
)
83+
84+
if proc.returncode != 0:
85+
self.log_error("Failed to run the ansible module")
86+
self.log_error("Ansible INI module returned: %s" % proc.stdout)
87+
self.log_error("Ansible INI module returned: %s" % proc.stderr)
88+
return (
89+
Result.NOT_KEPT,
90+
[],
91+
)
92+
93+
self.log_debug("Received output: %s (stdout)" % proc.stdout)
94+
self.log_debug("Received output (stderr): %s" % proc.stderr)
95+
return (
96+
Result.KEPT,
97+
[],
98+
)
99+
100+
101+
if __name__ == "__main__":
102+
AnsiballINIModule().start()

0 commit comments

Comments
 (0)