Technique reference

A technique is described by a XML file that lists:

  • the template files

  • the sections of the technique

  • the variables that must be defined

  • the compatibility list

Files organisation

The techniques are ordered in Categories. A Category is described by a category.xml file, that defines the name and description of a category. A Category can contain other Categories, or Techniques. A Technique is versioned, and can exist in several versions. The description of a Technique is the metadata.xml file.

techniques
+--- category.xml
+--- fileConfiguration
|   +--- category.xml
|   +--- security
|   |   +--- filesPermissions
|   |   |   +--- 1.0
|   |   |   |   +--- permlist.st
|   |   |   |   +--- metadata.xml
|   |   |   |   +--- filesPermissions.st
|   |   +--- category.xml
|   |   +--- sudoCheck
|   |   |   +--- 2.0
|   |   |   |   +--- metadata.xml
|   |   |   |   +--- sudoCheck.st
|   |   |   +--- 1.0
|   |   |   |   +--- metadata.xml
|   |   |   |   +--- sudoCheck.st

metadata.xml and Techniques templates (*.st)

These files must reside in a folder with a version number. For each Technique, there can be several versions, Rudder will let you choose the version when creating a Directive.

Version number formatting

The version number follows a formatting "a la Debian" as described here: https://www.debian.org/doc/debian-policy/index.html#s-f-version, (without the debian_revision version)

General Rules

All the tag name in the .xml are in upper case, all the attributes are in camel case:

<SECTION name="example" component="true" componentKey="variable_name">

Details of the metadata.xml file

<TECHNIQUE id="technique_unique_id" name="human_name_of_the_technique">
  <DESCRIPTION>Description of the Technique</DESCRIPTION>
  <LONG_DESCRIPTION>Long description of the technique</LONG_DESCRIPTION>
  <DEPRECATED>Deprecation message</DEPRECATED>                <!-- Mark the Technique as deprecated, deprecation message is mandatory, Only available since Rudder 3.0 -->
  <DISPLAY>true/false</DISPLAY>                               <!-- Define if the Technique is displayed in the interface or not. Default value : true -->
  <COMPATIBLE>                                                <!-- Optional, describe the version of the OS and Agent the Technique has been tested on. Only for information purpose -->
    <OS version=">=2.5">OS Name</OS>                          <!-- Optional; OS Name and version on which the Technique has been tested -->
    <AGENT version=">=3.6">cfengine-community</AGENT>         <!-- Optional; Agent name and version on which the Technique has been tested -->
  </COMPATIBLE>
  <MULTIINSTANCE>true/false</MULTIINSTANCE>                   <!-- Optional; defines if several instances of this template with differents variables can be deployed on a node; default value: false -->
  <POLICYGENERATION>separated</POLICYGENERATION>              <!-- Optional; defines if each Directives based on this Technique will be in a separated folder; default value: false. Note, if this parameter is set, you'll have to use the RudderUniqueID special placeholder to avoid duplicate names for methods and bundles -->

  <SYSTEM>true/false</SYSTEM>                                 <!-- Optional, defines if this Technique is a system Technique (internal Rudder usage); default value: false -->
  <BUNDLES>                                                   <!-- List of the bundles that must be included in the bundlesequence -->
    <NAME>BundleName</NAME>
  </BUNDLES>
  <TMLS>                                                      <!-- List of all the templates defined by this Technique -->
   <TML name="tmlName">                                       <!-- Container for a TML (without the trailing .st -->
    <OUTPATH>relativ/path/of/file</OUTPATH>                   <!-- Optional; defines the relative path for the generated file for this template; default: techniqueName/version/tmlName.cf -->
    <INCLUDED>true/false</INCLUDED>                           <!-- Optional; defines if the template must be in the inputs list of the generated promises; default: true -->
   </TML>
  </TMLS>
  <FILES>                                                     <!-- List of files to be copied "as-is" with this Technique. StringTemplate parser is NOT used on these. -->
    <FILE name="file.txt">                                    <!-- Container for a FILE. name (mandatory) = path to the file to copy, can be relative or absolute from RUDDER_CONFIGURATION_REPOSITORY/ (see below) -->
    <FILE name="file2.txt"><OUTPATH>technique_name/newname.txt</OUTPATH></FILE>
    <FILE name="RUDDER_CONFIGURATION_REPOSITORY/directory/other/file.txt"><OUTPATH>technique_name/filename</OUTPATH></FILE>
  </FILES>
  <TRACKINGVARIABLE>                                          <!-- Defines a special system variable TRACKINGKEY that contains all the necessary information to track which Directive generated the policies -->
    <SAMESIZEAS>VariableName</SAMESIZEAS>                     <!-- Optional; defines the cardinality of this variable based on the cardinality of the VariableName -->
  </TRACKINGVARIABLE>

  <RUNHOOKS>                                                  <!-- Optionnal; defines the list of pre and post hooks for techniques with separated policy generation mode -->
    <PRE bundle="pre_hook_bundle">                            <!-- Optionnal; name of the bundle/method to run before any directive based on this technique is run -->
      <REPORT name="Component name"/>                         <!-- Component Name for the report of the hook
      <PARAMETER name="name" value="value"/>                  <!-- Optionnal; parameter to pass to the hook
    </PRE>
    <POST bundle="post_hook_bundle">                          <!-- Optionnal; name of the bundle/method to run after all the directives based on this technique are run -->
      <REPORT name="Component name"/>                         <!-- Component Name for the report of the hook
      <PARAMETER name="name" value="value"/>                  <!-- Optionnal; parameter to pass to the hook
    </POST>
  </RUNHOOKS>


  <SECTIONS>                                                  <!-- Lists all the sections of the policies -->
    <SECTION name="sectionName">                              <!-- Container of a section (see below) -->
    </SECTION>
  </SECTIONS>

</TECHNIQUE>

The <SECTION> tag

In a metadata.xml, there can be only one SECTIONS tag, that encloses one or several SECTION tags. A SECTION tag contains variables declaration and subsections. A SECTION can contain Variables definitions and SECTION.

<SECTION name="sectionName" multivalued="true/false" component="true/false" componentKey="variableName/None">

A SECTION has the following attributes:

  • name: mandatory, the name of the section

  • multivalued: optional, default false, defines if the section is repeatable or not. If so, the Web Interface will display a "Add another" and "Delete" button for this section

  • component: optional, default false; defines if the section is a component, and if true, the section will appear in the reporting, with its section name

  • componentKey: optional, default None; defines the variable that is the key of the component. Note that the componentKey can only be defined if component is true

  • displayPriority: optional, default high; defines if the section is displayed by default (high) or hidden by default (low)

A multivalued section can only contain variable, and cannot contain section
If there are no SECTION defined with component="true", a default SECTION for reporting will be generated, named after the id of the Technique (the folder name of the Technique)

Variables definitions in the <SECTION> tags

There are three tags to create a variable:

  • SELECT1: Can select only one value out of several. If there are less than 3 possible values, displays radio buttons, otherwise a select field.

  • SELECT: Can select several values out of al the possibles. Displays checkboxes.

  • INPUT: Displays an input field (that can be tuned)

<SELECT1/SELECT/INPUT>                                                        <!-- Depend on the display and behaviour needed -->
  <NAME>variableName</NAME>
  <DESCRIPTION>variableDescription</DESCRIPTION>
  <LONGDESCRIPTION>longDescription</LONGDESCRIPTION>                          <!-- Optional, set the text in the tooltips -->
  <UNIQUEVARIABLE>true/false</UNIQUEVARIABLE>                                 <!-- Optional, default false; if true, this variable will have the same value over all the instance of this template for a given node -->
  <ITEM>                                                                      <!-- Only for SELECT and SELECT1, list of selectable values -->
    <VALUE>value</VALUE>                                                      <!-- value that will be put in the template-->
    <LABEL>humanReadableText</LABEL>                                          <!-- value displayed in the web interface -->
  </ITEM>
  <CONSTRAINT>                                                                <!-- Optional, defines some constraints on values -->
    <DEFAULT>defaultValue</DEFAULT>                                           <!-- Optional; Defines a default value -->
    <TYPE>variableType</TYPE>                                                 <!-- Optional; default string; variable type -->
    <MAYBEEMPTY>true/false</MAYBEEMPTY>                                       <!-- Optional; default false; defines if the variable is optional or not; only for the INPUT variable -->
    <REGEX error="errorMsg">regex</REGEX>                                     <!-- Optional; only for the INPUT variable; efine a regular expression the variable should match, and an optional error message -->
    <PASSWORDHASH>hashtype</PASSWORDHASH>                                     <!-- Optional; only for the password TYPE variable; define the way a password will be handled (hashed or not, hash types allowed ...) -->
  </CONSTRAINT>
</SELECT1/SELECT/INPUT>

Note: It is possible to inline LABEL and VALUE in the ITEM tag

<ITEM label="Red" value="red"/>

is equivalent to

<ITEM>
 <LABEL>Red</LABEL>
 <VALUE>red</VALUE>
</ITEM>
INPUT fields are automatically escaped, meaning any quote will be written in the policies as \" ; and any backslash will be written as \\

Available types for an INPUT variable

  • string : any string is accepted (no specific displayer)

  • textarea : accept any strings, but use a textarea in place of the input text.

  • perm : display a matrix of read/write/execute by user/group/all

  • integer : only accept integers

  • datetime : display a JQuery calendar and check date format

  • boolean : display a checkbox

  • mail : only accept emails

  • ip : only accept ips. Before Rudder 3.1.14, 3.2.7 and 4.0.0, "ip" was accepting only IPv4 ip. Since these releases, it accepts both IPv4 and IPv6 format. <br />

  • ipv4 [since Rudder 3.1.14, 3.2.7, 4.0.0]: only accept IPv4 formatted IPs

  • ipv6 [since Rudder 3.1.14, 3.2.7, 4.0.0]: only accept IPv6 formatted IPs

  • size-<unit> : (size-b, size-kb, size-mb, size-gb ou size-tb)

  • raw : the content of this field will not be escaped when written in the policies (Rudder >= 2.6)

  • password : the content of this field will be handled as a password, and thus be hidden and transformed if necessary (see "Password handling" below)

The <FILES> tag

Example:

<FILES>
<FILE name="file.txt"><OUTPATH>foo/bar/other-name.txt</OUTPATH></FILE>
<FILE name="RUDDER_CONFIGURATION_REPOSITORY/some/absolute/file.txt"><OUTPATH>foo/bar/some-name.txt</OUTPATH></FILE>
</FILES>
  • name is mandatory. It’s the path to file to copy, either relative to the technique directory (i.e, at the same level as metadata.xml) or absolute from the configuration repository directory if it starts with RUDDER_CONFIGURATION_REPOSITORY (usually /var/rudder/configuration-repository) (and yes, this forbids the use case where you want to have a sub-directory named RUDDER_CONFIGURATION_REPOSITORY under the technique directory - I’m sure one will find other way to do it if really needed :). The file will be taken from git, at the same git revision as other techniques files.

  • OUTPATH is optional. If not specified, the file will be copied into the target node policies at the same place as other files for the technique, with the same name. If specified, you have to give a path+name, where path is relative to the directory for agent promises on the node (i.e, if you want to put the file in the technique directory, you need to use techniqueName/new-file-name.txt)

Examples

Multivalued sections

In the "NFS Client settings" Technique, there is a multivalued section with several entries. Here is a partial extract from it, with

  • A multivalued section, named NFS mount point, that is multivalued and is a component. The variable reference for this component (the key) is NFS_CLIENT_LOCAL_PATH

  • One SELECT1 field, that will show two radio buttons, Mount and Unmount, with the default value to Mount

  • One INPUT field, named NFS_CLIENT_LOCAL_PATH, that is a text

 <SECTION name="NFS mountpoint" multivalued="true" component="true" componentKey="NFS_CLIENT_LOCAL_PATH">
     <SELECT1>
       <NAME>NFS_CLIENT_UMOUNT</NAME>
       <DESCRIPTION>Which operation should be done on this mountpoint</DESCRIPTION>
       <ITEM>
         <LABEL>Mount</LABEL>
         <VALUE>no</VALUE>
       </ITEM>
       <ITEM>
         <LABEL>Unmount</LABEL>
         <VALUE>yes</VALUE>
       </ITEM>
       <CONSTRAINT>
         <DEFAULT>no</DEFAULT>
       </CONSTRAINT>
     </SELECT1>
     <INPUT>
       <NAME>NFS_CLIENT_LOCAL_PATH</NAME>
       <DESCRIPTION>Local path to mount the remote on</DESCRIPTION>
     </INPUT>
  ...
 </SECTION>

Unique variable across several instance

This variable can have only one value, over all the instances of this Technique, on a node

  <SECTIONS>
      <INPUT>
        <NAME>UNIQUE</NAME>
        <DESCRIPTION>Unique variable</DESCRIPTION>
        <CONSTRAINT>
          <TYPE>string</TYPE>
        <CONSTRAINT>
        <UNIQUEVARIABLE>true</UNIQUEVARIABLE>
    </INPUT>
  </SECTIONS>

Password handling

The password type allows to show an input text field whose content will be hashed when the form is submitted so that the password is never store in clear text.

Directive Password Field

Available hash formats

For now, the password field support these hash algorithms :

  • PLAIN : that is not an hash algorithm, it just save the password in plain text, as inputted by the user.

  • MD5, SHA1, SHA256, SHA512 : uses the matching hash algorithm

  • LINUX-SHADOW-MD5, LINUX-SHADOW-SHA256, LINUX-SHADOW-SHA512 : build a string compatible with the Linux /etc/shadow format, as "specified" in http://man7.org/linux/man-pages/man3/crypt.3.html

Technique metatdata content

To configure a password, you must specify two things in the <CONSTRAINT> section of the field:

  • <TYPE>password</TYPE> : use the password type

  • <PASSWORDHASH>comma,separated,list,of,hash</PASSWORDHASH> : specify the list of hash algo from witch the user will be allowed to choose.

  • Available algorithm names are the ones from the section above (case insensitive).

  • Choices are presented in order given by the list, the first being the default one.

  • If the list contains only one algo, the drop down select if change to a phrase saying to the user that the given algo will be used.

  • The list can not be empty. Moreover, if the <MAYBEEMPTY> constraint is set to false, the "None" option is not displayed to the user.

Password field definition example

<SECTION name="Password" component="true" componentKey="USERGROUP_USER_LOGIN">
    <INPUT>
        <NAME>USERGROUP_USER_PASSWORD</NAME>
        <DESCRIPTION>Password for this account</DESCRIPTION>
        <CONSTRAINT>
            <MAYBEEMPTY>true</MAYBEEMPTY>
            <TYPE>password</TYPE>
            <PASSWORDHASH>linux-shadow-md5,linux-shadow-sha256,linux-shadow-sha512</PASSWORDHASH>
        </CONSTRAINT>
    </INPUT>
</SECTION>

Separated policy generation

In Rudder 4.3, a new mode of policy generation is introduced, that allows to mix Audit and Enforce mode for Directives based on the same Technique on a given node, and have Directives based on different version of the same Techniques. It is enabled with the entry <POLICYGENERATION>separated</POLICYGENERATION> in metadata.xml, and result on separated generated files for each Directives. One directory is generated by Directive, in the path TechniqueName/TechniqueVersion_DirectiveID, and requires the use of a placeholder, RudderUniqueID, used in bundle/method name, as well as result classes, to avoid name and classes collision at runtime.

Usage of RudderUniqueID

Here is an extract from Technique to exhibit the use of the placeholder

bundle agent sudo_parameter_edit_sudoers_RudderUniqueID(filename, entity, nopasswd, alldo, command)
{
  vars:

      "index" slist => getindices("${entity}");

    pass1::

      "command_all[${index}]" string => "ALL=(ALL) NOPASSWD:ALL",
                      ifvarclass => "(sudo_${index}_RudderUniqueID_alldo.sudo_${index}_nopasswd).(sudo_${index}_RudderUniqueID_command_notempty|sudo_${index}_RudderUniqueID_alldo)";

It is used here in the bundle name, to ensure its unicity, and it is also used in the class name.

Pre and post hooks

Some Techniques require actions to be performed only once, before and/or after all operation (for instance, ensuring that a package is installed before configuring this package). A pre and post hook mechanism has been introduced, for these uses. By convention, all pre and post hooks are located in the hooks.st file, in the System Technique common (so outside of the Technique we consider, to enforce that only one version of the hook may live in the Technique repository at a time).

Hooks have only one parameter, which is a JSON entry, in the format

{
  "parameters":
    {
       "parameterName1":"parameterValue1",
       "parameterName2":"parameterValue2",
    },
  "reports":
    [
      {"id":"DirectiveId1","mode":"enforce/audit", "technique":"techniqueName", "name":"componentName", "value":"componentValue1"},
      {"id":"DirectiveId2","mode":"enforce/audit", "technique":"techniqueName", "name":"componentName", "value":"componentValue2"},
      {"id":"DirectiveId3","mode":"enforce/audit", "technique":"techniqueName", "name":"componentName", "value":"componentValue3"},
   ]
}

The entries parameterName and parameterValue are defined by the PARAMETER tag of the section RUNHOOKS of metadata.xml, while the componentName is defined by its REPORT tag.

Known limitations

There are several known limitations at the moment, that are acknowledged, and will be solved in a "not too distant" future:

Can’t put a multivalued section in a multivalued section

It is not possible, due to limitation in the format in which the variable’s values are stored in the LDAP tree, to put multivalued sections within multivalued sections.

Can’t have several multivalued sections that are components with keys

For the moment, there is only one TRACKINGKEY, so it is not possible to have several multivalued sections that have keys.

Can’t have several sections that are components with keys in multivalued Techniques.

It is a side effect of the previous limitation.

Syntax of the Techniques

Generalities

The Techniques use the StringTemplate engine. A Technique must have the .st extension to be extended by Rudder (have some variables replaced, some part removed or added given some parameters).

Variable replacement

Note : Rudder use a StringTemplate grammar slightly different from the default one. Rather than using "$" as a variable identifier, the Techniques use "&" to avoid collision with the CFEngine variables

Single-valued variable replacement

&UUID&
  • Will be replaced by the value of the variable UUID

Replacement of variable with one or more values

&DNS_RESOLVERS: { "&it&" };separator=", "&
  • Will be replaced by "8.8.8.8", "8.8.4.4"

  • Here, &it& is an alias for the current item in the list (with no confusion, because there is only one variable)

&POLICYCHILDREN, CHILDRENID : {host, uuid |
"/var/rudder/share/&uuid&/"
maproot => { host2ip("&host&"), escape("&host&") },
admit => { host2ip("&host&"), escape("&host&") };

} &
  • host is an alias for the current value of POLICYCHILDREN

  • uuid is an alias for the current value of CHILDRENID

  • Both item are iterated at the same time, so both list must have the same length

Replacement of variable with one or more value, and writing an index all along

&FILE_AND_FOLDER_MANAGEMENT_PATH:{path |"file[&i&][path]" string => "&path&";
}&
  • i is an iterator, starting at 1

The result would be:

"file[1][path]" string => "/var";
"file[2][path]" string => "/bin";

Conditional writing of a section

&if(INITIAL)&

something

&endif&

The variable must either be:

  • A boolean: If its value is true, then the section will be displayed

  • A variable with the parameter MAYBEEMPTY="true": If the value is not set, then the section won’t be displayed, otherwise it will be displayed

Unique identifier of Directive for Techniques with separated policy generation

As of Rudder 4.3, Techniques with separated policy generation (see tag POLICYGENERATION in metadata.xml) need to have a way to identify uniquely their generated files, and bundles and methods. The special placeholder RudderUniqueID is replaced at generation by the identifier of the Directive. It can be used anywhere in the .st files, or even in the OUTPATH.

Best Practices for Techniques

Naming convention

  • The name of bundle and classes should be written with underscore (i.e: this_is_a_good_example) instead of CamelCase (i.e: ThisIsABadExample)

  • All variable, class and bundle names should be prefixed by "rudder_"

  • The bundle entry point for the Technique should be named rudder_<name_of_the_technique>

  • The bundles which makes all the actions should be suffixed by a meaningful name ( "rudder_<name_of_the_Technique>_installation", "rudder_<name_of_the_Technique>_configuration", "rudder_<name_of_the_Technique>_reporting", ..). This rule applies even if there is only one bundle

  • The prefix of classes should all be "rudder_<name of the Technique>_"

  • The classes defined as an outcome should be named:

  • rudder_<name of the Technique>_<action>_kept

  • rudder_<name of the Technique>_<action>_repaired

  • rudder_<name of the Technique>_<action>_failed

  • rudder_<name of the Technique>_<action>_denied

  • rudder_<name of the Technique>_<action>_timeout

  • rudder_<name of the Technique>_<action>_error (error include failed, denied and timeout)

  • The name of the bodies written in the Rudder Library should be prefixed: rudder_common_

Raising classes

  • rudder_<name of the Technique>_<action>_error should be raised simultaneously as rudder_<name of the Technique>_<action>_failed, rudder_<name of the Technique>_<action>_denied or rudder_<name of the Technique>_<action>_timeout.

  • The body rudder_common_classes automatically abide by this rule

Writing convention

Technique naming guidelines

The following rules should be followed when naming a new Technique:

  • Try to keep names as short as possible, to improve readability

  • Read the existing technique list, and particularly techniques related to what you are writing. The new names should be consistent with existing ones.

  • The name should be a nominal group, use "File content" and "Service state" but never "Manage file content" or "Set Service state". It describes the target of the action, not the action itself.

  • The name should look like: General Concept (package, file, etc.) + Source (from file, etc.) + Implementation details (platform, software name, etc.)

  • Package sources (Zypper)

  • HTTP server (Apache)

  • Variable from local file (string)

  • The general idea is to go from the most general information to the most precise.

  • Use "directory" and never "folder"

  • Use "settings" and never "configuration"

  • Use sentence case, only the first word is capitalised, like in a normal sentence ("Variable from local file" and not "Variable from Local File").

In the Technique

  • We try to follow CFEngine conventions but with some exceptions like using brackets "{}" instead of parenthesis "()"

  • When defining bundles or bodies, the opening bracket should be on a dedicated line. Example:

bundle common control
{
  bundlesequence => { "exemple" };
}
  • Indentation should be made by spaces. A incrementation of indentation is equal to two spaces

  • The policy type should be indented by two spaces (instead of being at the same indentation level than the bundle name)

  • The class expression should be indented by four spaces (two spaces after the policy type)

  • The promiser should be indented by six spaces (two spaces after the class expression or four spaces after the policy type if no class expression is defined)

  • Attributes of policies should be indented by eight spaces (two spaces after the promiser) and it should be only one attribute by line.

  • Attribute’s arrows '⇒' should all be at the same level, one character after the largest attribute name

bundle agent example
{
  type:
      "promiser"
        attribute  => "value1";

    class::
      "promiser2"
        attribute2 => "value2";
}
  • Attributes of policy type "vars" and "classes" should be on only one line except if there are more than one attribute.

  • For policy type "vars" and "classes" on one line, attribute names and the arrows should be aligned

  • A list should be written on multiple lines if it needs more than 80 characters in one line

  • Multi-line lists should have comma after each element, except the last one.

  • Multi-line lists should begin with only a bracket "{"

    vars:
        "value" slist =>
          {
            "one",
            "two",
            "three"
          };
  • The name of the variable in argument of the bundle should be named "params"

  • The call of the variables should be made with by using brackets ${var_correctly_called} instead of parenthesis $(var_wrongly_called)

  • Alternation of brackets and parenthesis are tolerated when lots of variables are nested for more readability: ${var_lv1[$(var_lvl2[${var_lvl3}])]}

  • A Technique should have its bundle wrote with parameters

  • All the bundles should have as first argument "prefix" which contains the prefix to use for all the classes made from an outcome. This prefix should never be hardcoded in the bundle.

  • Always write comments with # when a policy needs more than 30 seconds of thought.

  • If classes should be created in order to iterate for make a workaround of the normal ordering (i.e: "pass1", "pass2", "pass3"), they should always be defined at the end of the policy type "classes".

  • The order to the policy type must always be in the order of the normal ordering : https://docs.cfengine.com/docs/3.10/reference-language-concepts-normal-ordering.html

  • StringTemplate variables should always be written in UPPERCASE

  • StringTemplate variables should be written with underscore

  • StringTemplate variables should always be prefixed by the Technique name in uppercase too. i.e: CHECK_GENERIC_FILE_FILE_NAME

In the metadata.xml

  • Name of sections should always be written in literary English (no CamelCase or underscores).

  • The value of variable "Don’t change" should always be "dontchange" or "" if the easier.

Files convention

  • File names in a Technique should not be prefixed by the name of the Technique

  • When a Technique needs specific bodies, the bodies should be written in a bodies.st file

  • The file containing the bundle which makes all the actions (and containing the bundle "run") should be named "main.cf"

  • The file containing all the variables and calling the bundle "run" should be name config.st

  • Initialization of a new Technique should always be made from the file "technique-metadata-sample.xml" which is present on the root of the "rudder-techniques" repository

  • Rudder standard library should be located in "common" Technique

Maintenance

  • Always follow the conventions above when Techniques are updated but only for the lines edited. This rule concerns the Techniques on all the branches of git.

  • On any branches that have released versions on them, we only allow minimal modifications. No lines should be modified if not to fix a bug (respecting these best practices is not currently considered a bug).

Testing

  • There is a test suite in scripts/check-techniques.sh that check metadata.xml and normal ordering in code

  • The list of all maintained techniques (techniques and versions) is in maintained-techniques file, and should be updated when new techniques or versions are created.