Skip to content

Configuration Commands

poblahblahblah edited this page Sep 12, 2014 · 5 revisions

Etch was originally focused on managing the exact state of things on disk, file contents and metadata like permissions and ownership, symlinks and directories. You could execute commands to manipulate configuration, but only as a side-effect to managing the state of a specific file.

However, some system configuration must by done solely via the execution of a command. Solaris loves this sort of thing, but it exists elsewhere too. Package installation or removal is another example.

As such we've added support for "configuration commands" to etch.

The idea is that you specify both "what command should I run" and "when should I run it". I.e. the "command" and the "guard". You test the guard, and if false (indicating that things are not yet properly configured) you run the command, then re-run the guard to ensure that things are now correct. Some commands should only be run once, or only if something is misconfigured, so the guard provides a way for the user to test for that and avoid running the command if things are properly configured and working.

Configuration for these "configuration commands" lives within a commands/ directory at the top of the etch server configuration hierarchy. You create arbitrarily named directories within that commands/ directory and place a commands.yml in each one. Unlike the source/ directory where the directory structure maps to the file being generated there's no such obvious naming convention here, so you can name the directories in any way that makes sense to you, i.e. solaris_printing or linux_packages or solaris_ldapclient.

The directory structure in visual form:

  • commands/
    • linux_packages/
      • commands.yml
    • solaris_ldapclient/
      • commands.yml
  • config.dtd
  • defaults.yml
  • nodes.yml
  • source/

Some examples:

Package management:

steps:
- step:
    guard: rpm --quiet -q emacs
    command: yum -y install emacs
- step:
    guard: rpm --quiet -q xterm
    command: yum -y install xterm

Package management for multiple Linux distributions:

steps:
- step:
    guard:
      - where operatingsystem =~ Debian|Ubuntu: dpkg-query -Wf'${Status}' sl 2> /dev/null | grep -q '^i'
    command:
      - where operatingsystem =~ Debian|Ubuntu: apt-get install -y sl

- step:
    guard:
      - where operatingsystem =~ Debian|Ubuntu: dpkg-query -Wf'${Status}' git 2> /dev/null | grep -q '^i'
      - where operatingsystem =~ CentOS|RedHat: rpm --quiet -q git-core
    command:
      - where operatingsystem =~ Debian|Ubuntu: apt-get install -y git
      - where operatingsystem =~ CentOS|RedHat: yum -y isntall git-core

Solaris LDAP client configuration:

steps:
- step:
    guard: ldapclient list | grep NS_LDAP_SEARCH_BASEDN= dc=example,dc=com
    command: ldapclient manual -a defaultSearchBase=dc=example,dc=com -a defaultServerList="ldap1.example.com ldap2.example.com"

Commands can depend on other commands and/or files:

depend:
- othercommands
dependfile:
- /etc/passwd
steps:
- step:
    guard: false
    command: true

Older XML Format

The older XML format for commands.xml files is still supported.

Acknowledgments

I was introduced to this concept by Trey Harris' "A New Approach to Scripting" presentation at LISA '04. He created an implementation in Perl called Commands::Guarded. His documentation explains the concept very lucidly so I have not attempted to recreate that overall explanation of the concept here. He in turn references Edsger W. Dijkstra's 1975 paper Guarded commands, nondeterminacy and formal derivation of programs.