Skip to content

Latest commit

 

History

History
223 lines (151 loc) · 9.08 KB

HACKING.md

File metadata and controls

223 lines (151 loc) · 9.08 KB

How to contribute to CFEngine

Thanks for considering contributing to the CFEngine! We take pull-requests on GitHub at http://github.com/cfengine, and we have a public Redmine bug-tracker at http://bug.cfengine.com

Normally, bug fixes have a higher chance of getting accepted than new features, but we certainly welcome feature contributions. If you have an idea for a new feature, it might be a good idea to open up a feature ticket in Redmine first to get discussion going.

Top reasons pull-requests are rejected or delayed

  • Code does not follow style guidlines. (See section on Coding Style)

  • Pull request addresses several disparate issues. In general, smaller pull-requests are better because they are easier to review and stay mergeable longer.

  • Messy commit log. Tidy up the commit log by squashing commits. Write good commit messages: One line summary at the top, followed by an optional detailing paragraphs. Please reference Redmine tickets, e.g. "Close #1234"

  • Code is out-of-date, does not compile, or does not pass all tests. Again, focused and small pull-requests are better.

  • No attached test case. Normally, all new code needs test cases. This means a functional test runnable with 'make check'.

Code Overview

The CFEngine codebase can be usefully thought of as a few separate components: utilities (libutils), parsing (libpromises), evaluation (libpromises), actuation (mostly in cf-agent), network (libcfnet).

Over the past year, the structure of the codebase has undergone some change. The goal of the restructuring is to isolate separate components with explicit dependencies, and provide better unit test coverage.

For a general introduction to the tools, please read the man pages.

libcompat

These are replacement functions in cases where autoconf cannot find a function it expected to find on the platform. CFEngine takes an approach of relying on the platform (typically POSIX) as much as possible, rather than creating its own system abstraction layer.

libutils

Contains generally useful datastructures or utilities. The key point about libutils is that it is free of dependencies (except libcompat), so it does not know about any CFEngine structures or global state found in libpromises.

  • sequence.h: Loosely based on glib GSequence, essentially an array list.
  • map.h: General purpose map (hash table).
  • set.h: General purpose set, a wrapper of Map.
  • writer.h: Stream writer abstraction over strings and FILEs.
  • xml_writer.h: Utility for writing XML using a Writer.
  • csv_writer.h: Utility for writing CSV using a Writer.
  • buffer.h: Dynamic byte-array buffer.
  • json.h: JSON document model, supports de/serialization.
  • string_lib.h: General purpose string utilities.
  • logging.h: Log functions, use Log() instead of printf.
  • man.h: Utility for generating the man pages.
  • list.h: General purpose linked list.
  • ip_address.h: IP address parsing.
  • hashes.h: Hashing functions.
  • file_lib.h: General purpose file utilities.
  • misc_lib.h: Really general utilities.

libcfnet

Contains the networking layer for CFEngine. (At the time of writing, a bit of a moving target).

libpromises

This is the remainder of the old src directory, that which has not been categorized. The roadmap for the project remains to leave libpromises as a component for evaluation.

  • cf3.defs.h: Contains structure definitions used widely.
  • env_context.h: Header for EvalContext, keeper of evaluation state.
  • dbm_api.h: Local database for agents.
  • mod_.c: Syntax definitions for all promise types (actuation modules).
  • syntax.h: Syntax utilities and type checking.
  • *files_.h": File utilities we haven't been able to decouple from evaluation.
  • locks.h: Manages various persistent locks, kept in a local database.
  • rlist.h: List for Rvals (of attributes).
  • expand.c: Evaluates promises.
  • parser.h: Parse a policy file.
  • policy.h: Policy document object model, essentially the AST output of the parsing stage.
  • sysinfo.c: Detects hard classes from the environment.
  • evalfunction.c: Where all the built-in functions are implemented.
  • crypto.h: Crypto utilities for some reason still tied to evaluation state.
  • generic_agent.h: Common code for all agent binaries.

Things you should not use in libpromises

  • cf3.extern.h: Remaining global variables.
  • prototypes3.h: The original singular header file.
  • item_lib.h: Item is a special purpose list that has been absued for unintended purposes.
  • assoc.h: An lval-rval pair, deprecated in favor of EvalContext symbol table.
  • scope.h: Old symbol table, this will move into EvalContext.

cf-agent

The binary cf-agent and contains most actuation logic in the verify_.h files. Each file more or less maps to a promise type.

As an example, the file verify_packages.h contains *VerifyPackagesPromise(EvalContext *ctx, Promise pp).

cf-monitord

Monitoring probes are contained in mon_.c files. These all have a common header file mon.h.

Lifecycle of cf-agent

The following outlines the normal execution of a cf-agent run.

  1. Read options and gather these in GenericAgentConfig.
  2. Create an EvalContext and call GenericAgentConfigApply(ctx, config).
  3. Discover environment and set hard classes, apply to EvalContext.
  4. Parse input policy file, get a Policy object.
  5. Run static checks on Policy object.
  6. Evaluate each Bundle in bundlesequence.
  7. Write reports to disk.

Bootstrapping cf-agent

The following outlines the steps taken by agent during a successful bootstrap to a policy server.

  1. Resolve IP of policy server.
  2. Remove existing files in outputs.
  3. Write built-in failsafe.cf to outputs.
  4. Write IP of policy server to policy_server.dat, optionally also marker file am_policy_server.
  5. Proceed using failsafe.cf as input file. 5a. Evaluating failsafe.cf, fetches policy files from the policy server. 5b. Evaluating failsafe.cf, starts cf-execd.
  6. Agent finishes.
  7. cf-execd continues to run cf-agent periodically with policy from /inputs.

Coding Style

Output Message Conventions

CFEngine outputs messages about what its doing using the Log function. It takes a LogLevel enum mapping closely to syslog priorities. Please try to do the following when writing output messages.

  • Do not decorate with ornamental symbols or indentation in messages. Leave formatting to Log.

  • When quoting strings, use single quotes, e.g. "Some stuff '%s' happened in '%s'.

  • Keep in mind context, e.g. write "While copying, insufficient permissions" rather than "Insufficient permissions".

  • Use output sparingly, and use levels appropriately. Verbose logging tends to get very verbose.

  • Use platform indenpendent GetErrorStr when you want strerror(errno). Write e.g. Log(LOG_LEVEL_ERR, "Failed to open ... (fopen: %s)", GetErrorStr())

  • Normally, try to keep it one line, don't a message using several statements. Do not put newlines in the message.

  • Normally, do not circumvent Log by writing to stdout or stderr.

  • Use LOG_LEVEL_DEBUG for environmental situations, e.g. don't write "Entering function Foo()", but rather "While copying, got reply '%s' from server".

Testing

It is extremely important to have automated tests for all code, and normally all new code should be covered by tests, though sometimes it can be hard to mock up the environment.

There are two types of tests in CFEngine. Unit tests are generally preferable to acceptance tests because they are more targeted and take less time to run. All tests are run using make check.

  • Unit tests. Unit tests are a great way of testing some new module (header file). Ideally, the new functionality is written so that the environment can be easily injected and results readily verified.

  • Acceptance tests. These are tests that run cf-agent on a policy file that contains test and check bundles, i.e. it uses CFEngine to both make a change and check it. See also script tests/acceptance/testall.

Emacs users

There is Elisp snippet in contrib/cfengine-code-style.el which defines the project's coding style. Please use it when working with source code. The easiest way to do so is to add

(add-to-list 'load-path "/contrib") (require 'cfengine-code-style)

and run

ln -s contrib/dir-locals.el .dir-locals.el

in the top directory of the source code checkout.