- Usage
- Respect promise locks
- Don’t automatically include the stdlib
- Specify the bundlesequence
- Define additional classes
- Specify additional options to cf-agent
- Change the output log level
- Tangled SRC blocks
- Use a custom command for snippet execution
- Include command in the output
- Automatically wrap code in a
__main__
bundle
- Installation
The --no-lock
argument is passed by default. To use locks set the header
argument use-locks
to t
.
#+BEGIN_SRC cfengine3 :use-locks t bundle agent main { reports: "My Content"; } #+END_SRC
The standard library is included by default via body file control. To avoid this
define set include-stdlib
to nil
for example:
#+BEGIN_SRC cfengine3 :include-stdlib nil bundle agent main { reports: "My Content" printfile => cat($(this.promise_filename)); } #+END_SRC #+RESULTS: #+begin_example /home/nickanderson/org/cfengine3-15841vLB:6:0: error: Undefined body cat with type printfile error: Policy failed validation with command '"/home/nickanderson/.cfagent/bin/cf-promises" -c "/home/nickanderson/org/cfengine3-15841vLB"' error: CFEngine was not able to get confirmation of promises from cf-promises, so going to failsafe error: CFEngine failsafe.cf: /home/nickanderson/.cfagent/inputs /home/nickanderson/.cfagent/inputs/failsafe.cf error: No suitable server found error: No suitable server found R: Built-in failsafe policy triggered notice: Q: ".../cf-agent" -f /": error: No suitable server found Q: ".../cf-agent" -f /": error: No suitable server found Q: ".../cf-agent" -f /": error: No suitable server found #+end_example
#+BEGIN_SRC cfengine3 :include-stdlib t bundle agent main { reports: "My Content" printfile => cat($(this.promise_filename)); } #+END_SRC #+RESULTS: : R: My Content : R: body file control{ inputs => { '$(sys.libdir)/stdlib.cf' };} : R: bundle agent main : R: { : R: reports: : R: : R: "My Content" : R: printfile => cat($(this.promise_filename)); : R: }
You can specify the bundlesequence
by setting the bundlesequence
header. For
example:
#+BEGIN_SRC cfengine3 :bundlesequence one,two bundle agent one { reports: "Running bundle $(this.bundle)"; } bundle agent two { reports: "Running bundle $(this.bundle)"; } #+END_SRC #+RESULTS: : R: Running bundle one : R: Running bundle two
You can define additional classes by setting the define
header.
#+BEGIN_SRC cfengine3 :define EXTRA,MORE bundle agent main { reports: EXTRA:: "EXTRA Class defined"; MORE:: "EVEN MORE"; } #+END_SRC #+RESULTS: : R: EXTRA Class defined : R: EVEN MORE
You can use the :extra-opts
header argument to specify additional arbitrary options to pass to cf-agent
. The string is passed as-is.
#+BEGIN_SRC cfengine3 :extra-opts --show-evaluated-vars=default:main bundle agent main { vars: "test" string => "test string"; } #+END_SRC #+RESULTS: : Variable name Variable value Meta tags : default:main.test test string source=promise : default:maintain_key_values_meta.tags {"deprecated=3.6.0","deprecation-reason=Generic reimplementation","replaced-by=set_line_based"} source=promise
By default the agent is silent about repairs and only emits output on promises
that are not kept and promises that explicitly output information (like a
reports
type promise).
CFEngine 3.12.0 and newer can use the singular –log-level option to set the log
level to error
, warning
, notice
, info
, verbose
, or debug
.
NOTE: Setting log-level to debug
will not enable all log-modules which is done
when the debug boolean option is defined. To get as much information from the
agent run as possible, use the =debug= header argument.
#+BEGIN_SRC cfengine3 :log-level info
bundle agent example
{
commands:
"/bin/echo Hello World";
}
bundle agent __main__
{
methods:
"example";
}
#+END_SRC
#+RESULTS:
: info: Executing 'no timeout' ... '/bin/echo Hello World'
: notice: Q: ".../bin/echo Hello": Hello World
: info: Last 1 quoted lines were generated by promiser '/bin/echo Hello World'
: info: Completed execution of '/bin/echo Hello World'
#+BEGIN_SRC cfengine3 :info t
bundle agent main
{
commands:
"/bin/true";
}
#+END_SRC
#+RESULTS:
: info: Executing 'no timeout' ... '/bin/true'
: info: Completed execution of '/bin/true'
#+BEGIN_SRC cfengine3 :verbose t
bundle agent main
{
commands:
"/bin/true";
}
#+END_SRC
#+RESULTS:
#+begin_example
verbose: Could not open extension plugin 'cfengine-enterprise.so' from '/home/nickanderson/.cfagent/lib/cfengine-enterprise.so': (not installed)
verbose: Successfully opened extension plugin 'cfengine-enterprise.so' from '/var/cfengine/lib/cfengine-enterprise.so'
verbose: Successfully loaded extension plugin 'cfengine-enterprise.so'
verbose: CFEngine Core 3.11.0
verbose: ----------------------------------------------------------------
verbose: Initialization preamble
verbose: ----------------------------------------------------------------
... snipped for brevity
verbose: No lock purging scheduled
verbose: Outcome of version (not specified) (agent-0): Promises observed - Total promise compliance: 86% kept, 14% repaired, 0% not kept (out of 7 events). User promise compliance: 86% kept, 14% repaired, 0% not kept (out of 7 events). CFEngine system compliance: 0% kept, 0% repaired, 0% not kept (out of 0 events).
#+end_example
#+BEGIN_SRC cfengine3 :debug t
bundle agent main
{
commands:
"/bin/true";
}
#+END_SRC
#+RESULTS:
#+begin_example
debug: Trying to shlib_open extension plugin 'cfengine-enterprise.so' from '/home/nickanderson/.cfagent/lib/cfengine-enterprise.so'
debug: Could not open shared library: No such file or directory
verbose: Could not open extension plugin 'cfengine-enterprise.so' from '/home/nickanderson/.cfagent/lib/cfengine-enterprise.so': (not installed)
debug: Trying to shlib_open extension plugin 'cfengine-enterprise.so' from '/var/cfengine/lib/cfengine-enterprise.so'
debug: Could not open shared library: No such file or directory
verbose: Could not open extension plugin 'cfengine-enterprise.so' from '/var/cfengine/lib/cfengine-enterprise.so': (not installed)
debug: Setting hard class: default:debug_mode
debug: Setting hard class: default:opt_debug
debug: Setting hard class: default:verbose_mode
debug: Setting hard class: default:inform_mode
verbose: CFEngine Core 3.14.0a.ed0158a8e
#+end_example
By default, cfengine3 SRC
blocks are not tangled. To tangle the blocks to a
file, use the :tangle
header argument.
When a file is tangled, a shebang is automatically added to allow for easy
execution. To change or disable the shebang use the :shebang
header argument.
#+BEGIN_SRC cfengine3 :shebang :tangle /tmp/example.cf
bundle agent example
{
reports:
"CFEngine $(sys.cf_version)";
}
bundle agent __main__
{
methods:
"example";
}
#+END_SRC
#+BEGIN_SRC cfengine3 :shebang /var/cfengine-3.7/bin/cf-agent -f- :tangle /tmp/example.cf
bundle agent example
{
reports:
"CFEngine $(sys.cf_version)";
}
bundle agent __main__
methods:
"example";
}
#+END_SRC
By default, a body file control
to include the standard lib via
$(sys.libdir)
is included in exported files. This facilitates small snippets
of policy and does not interfere with a literate style of policy maintenance.
#+BEGIN_SRC term
~ $ cat /tmp/example.cf
#!/var/cfengine/bin/cf-agent -f-
body file control
{
inputs => { '$(sys.libdir)/stdlib.cf' };
}
bundle agent example
{
reports:
"CFEngine $(sys.cf_version)";
}
bundle agent __main__
{
methods:
"example";
}
#+END_SRC
By default tangled files are saved so that only the owner can read, write and
execute (700). Use the :tangle-mode
header argument to override the default.
NOTE: This differs from typical CFEngine policy permissions (600).
#+BEGIN_SRC cfengine3 :tangle /tmp/example.cf :tangle-mode (identity #o600)
bundle agent example
{
reports:
"CFEngine $(sys.cf_version)";
}
bundle agent __main__
methods:
"example";
}
#+END_SRC
Why does ob-cfengine3 add a shebang and set the tangled file to executable by default?
The authors workflow consists of many small examples and snippets that are
handed to many other people of varying organizations. Also, it’s a nifty trick
when combined with library __main__
bundles for running partial sets of policy
directly.
Why doesn’t ob-cfengine3 add a =bundle agent __main__= to the end of exported files?
It’s better suited for a snippet. It’s not useful unless it’s customized for the policy file.
#+BEGIN_SRC term
~ $ ./example.cf
R: CFEngine 3.13.0
error: Method 'example' failed in some repairs
~ $ ./example.cf -I
info: Can't stat file '/tmp/./example.cf.missing' on 'localhost' in files.copy_from promise
R: CFEngine 3.13.0
error: Method 'example' failed in some repairs
#+END_SRC
Note that you can use the :tangle-in-main
or :auto-main
options to automatically wrap the snippet in a __main__
bundle. See Automatically wrap code in a __main__
bundle for details.
By default, CFEngine code in a SRC block is executed using the cf-agent
command. This can be changed using the command
header argument. The command specified must accept the same arguments as cf-agent
.
For example, if you have a Docker image called which executes cf-agent
as the container’s ENTRYPOINT (see zzamboni/cf-agent for an example), you can have your code executed inside the container. Note that you have to take care that the path to which the temporary file is written by Emacs exists in the container as well. In this example, we specify the tmpdir
header argument to specify that the temporary file is written to /tmp
, and use the -v
option in docker run
to make the /tmp
directory visible within the container.
#+begin_src cfengine3 :command "docker run -v /tmp:/tmp zzamboni/cf-agent" :tmpdir /tmp
bundle agent main
{
reports:
"My hostname: $(sys.fqhost)";
}
#+end_src
#+RESULTS:
: R: My hostname: 5cd98f9265a8
Normally, the RESULTS block only contains any output produced by the execution of the CFEngine code. You can include the command in the output by specifying the command-in-result
header argument:
#+begin_src cfengine3 :command "docker run -v /tmp:/tmp zzamboni/cf-agent" :tmpdir /tmp :command-in-result t
bundle agent main
{
reports:
"Hello world!";
}
#+end_src
#+RESULTS:
: # docker run -v /tmp:/tmp zzamboni/cf-agent --no-lock --file /tmp/cfengine3-b7caCd
: R: Hello world!
The command is shown exactly as used by ob-cfengine3, which may depend on the header arguments you use. You can use the following additional arguments to customize the displayed command. Note that these options are purely cosmetic.
command-in-result-prompt
specifies the prompt to show before the command. Default is"# "
.command-in-result-command
specifies the command name to show. Defaults to the value ofcommand
(defaultcf-agent
).command-in-result-filename
specifies the filename to show. Defaults to the tangle filename if:tangle
is specified, otherwise to a temporary file path generated by Emacs.command-in-result-multiline
specifies whether to show the command split across multiple lines. Its value can b etrue
,false
orauto
. Default value isfalse
. In the case ofauto
, the determination is made depending on whether the command shown would be longer thancommand-in-result-maxlen
.command-in-result-maxlen
specifies the maximum length before splitting the command ifcommand-in-result-multiline
is given asauto
. Default is 80.
You can use these options to “prettify” the command shown, for example (contrast with the previous example):
#+begin_src cfengine3 :command "docker run -v /tmp:/tmp zzamboni/cf-agent" :tmpdir /tmp :command-in-result t :command-in-result-prompt "> " :command-in-result-command cf-agent :command-in-result-filename hello-world.cf
bundle agent main
{
reports:
"Hello world!";
}
#+end_src
#+RESULTS:
: > cf-agent --no-lock --file hello-world.cf
: R: Hello world!
Sometimes you may want to include in the document only the actual code to execute, without the surrounding bundle declaration, but still have it execute or tangle correctly. To control this, you have the following three header arguments:
:run-with-main
automatically wraps the contents of the SRC block in abundle agent __main__
before executing it;:tangle-with-main
automatically wraps the contents of the SRC block in abundle agent __main__
before tangling it;:auto-main
is a shortcut to enable both:run-with-main
and:tangle-with-main
.
#+begin_src cfengine3 :run-with-main yes :tangle-with-main yes :tangle /tmp/hello.cf reports: "Hello world!"; #+end_src #+RESULTS: : R: Hello world!
cat /tmp/hello.cf
#!/var/cfengine/bin/cf-agent -f-
bundle agent __main__
{
reports:
"Hello world!";
}
You can customize the template used to wrap the code in a bundle by setting the value of the ob-cfengine3-wrap-with-main-template
variable. Its default value is "bundle agent __main__\n{\n%s\n}\n"
.
Clone the repo, add it to your load path
(add-to-list 'load-path "~/src/ob-cfengine3/")
(require 'ob-cfengine3)
(ob-cfengine3 :repo "nickanderson/ob-cfengine3" :fetcher github)