Tales from the Gryphon/ blog/ 2009/ 02/ 24/

Tales from the Gryphon

Rethinking ucf

Manoj's hackergotchi
Tuesday 24 February
2009

There has been some discussion on the Debian development mailing list

Tortured activity diagram

about adding hooks into ucf, to allow people to do things like committing files into different SCM branches. So, I thought I would help people out by letting them tell me where hooks would be useful, and so decided to do an activity diagram for ucf. Gawd, what a mess. I mean, I wrote this thing, and it boggles even my mind. See the figure for how horrendous code can get when it grows organically.

So, I decided to re-factor/redesign ucf, see if I could create a less complex activity diagram. On analysis, it turns out that ucf has just five actions it may perform, and which action it takes depends on which of eight possible states the configuration file is in.

  • Actions
    • When called to do so, it can purge references to a configuration file from the database.
      • : Purge references to the configuration file from the database
    • When given a new file and a destination, ucf can take three actions
      • : Replace the configuration file at the destination path with the new file, and record the md5sum of the new file in the database
      • : Update the md5sum of the new file in the database, but not replace the destination file
      • : Do nothing.
      • : Ask the user whether to update the hash, or to replace the configuration file as well.

    That's it. Just five actions that ucf can take. Which of the four actions it takes is determined by a decision tree. The purge action is fairly simple, and the decision to take that route is unremarkable. When ucf can not decide on the proper action, it asks. The asking is the whole rationale for ucf, anyway.

  • State Machine

    It turns out there are eight different states in which the configuration file can be when ucf is called.

    • : This is a new configuration file, which has not been see by the system. In this case, there is no destination file, and there is no record of the file in the database.
    • This is a configuration file that has been seen on the system, but has gone missing (possibly due to user deletion). There are two sub cases:
      • : The new file is the same as one we have seen before (no change in upstream version), or
      • : There is a new upstream version of the file, and the user has deleted the local version.
    • This is a configuration file that has been seen on the system, and a version of the file is present at the destination. Now, there are a few cases here:
      • : All three files are the same (old, destination, new). There has been no change.
      • : The old and destination files are the same (no user change), but there has been a new upstream version.
      • : The new and destination files are the same (same changes locally and in upstream), but the old version is different.
      • : The new and old files are the same, but the destination file has been locally changed.
      • : All three files are different. The file at the destination has been modified (not the same as either the old or the new upstream file).
    • There is a destination file, but we have no record of it in the database. Perhaps this is a package newly converted to ucf, or the ucf DB was hosed. At this point, we try to determine whether the file at the destination corresponds to a historical version
      • If the maintainer has provided a file or a directory containing the hash of older versions of the configuration file, see if the file at the destination matches. If it does, This is case 3.1
      • If there is a default md5sum, instead of the directory or file, check against that. If there is a match, this is case 3.2
      • If there is no match, we have either a case 3.2 or a case 3.3
  • Behavior
    • Flags affecting behaviour

      There are three flags that determine the policy of ucf:

      • : Prefer the new configuration file always
      • : Prefer the old version always
      • : Recreate configuration file is missing. (overriding possible user deletion)
    • Disposition

      The disposition of the cases is as follows.

      • :: Purge references
        • 1.1 : Purge references to the file
      • :: New file or package.
        • 2.1: Replace File. We install the upstream version, regardless of the flags.
      • :: User deletion, no upstream changes
        • 2.1 : 3=1. Replace file. The user has specifically asked to have missing configuration files recreated.
        • 2.1 : 1=1: Replace file. The user has specifically asked for the new file to be used, no matter what.
        • 2.2 : 2=1: Update MD5sum.
        • 2.2 : Otherwise, Update MD5sum. The user deleted the configuration file, and nothing has changed since then.
      • :: User deletion, with upstream changes.
        • 2.1 : 3=1. Replace file. The user has specifically asked to have missing configuration files recreated.
        • 2.1 : 1=1: Replace file. The user has specifically asked for the new file to be used, no matter what.
        • 2.2 : 2=1: Update MD5sum.
        • 2.4 : Otherwise, Ask. While the user did delete the configuration file, but there have been upstream changes, so the user should be given an option to change their mind.
      • :: Nothing to do.
        • 2.3 : Take No Action
      • :: New upstream, no local changes
        • 2.1: 1=1: Replace file. The user has specifically asked for the new file to be used, no matter what.
        • 2.2 : 2=1: Update MD5sum.
        • 2.1 : Otherwise, Replace file. The user has not made any local changes, so the default is to upgrade the file.
      • :: Local and upstream in sync
        • 2.2: Update MD5sum, regardless of the flags.
      • :: No upstream changes
        • 2.1 : 1=1: Replace file. This is tricky. While there has been no upstream change, the user prefers to keep files the same as the upstream, so user changes are actually reverted here.
        • 2.2 : 2=1: Update MD5sum.
        • 2.2 : Otherwise, Update MD5sum. The user has made local changes, so the default is to not upgrade the file (in any case, there has been no change upstream, so nothing to upgrade, really).
      • :: Local and upstream changes. This is the meat-and-potatoes case.
        • 2.3: 2 == 1 : Take no action
        • 2.4: Otherwise, Ask. This is required by policy, so as to not override the user changes.
  • In Summary

    In Tabular form:

    Table 1: UCF Actions
    Case NONE 3 1 2
    1 1.1 1.1 1.1 1.1
    1 2.1 2.1 2.1 2.1
    2.1 2.2 2.1 2.1 2.2
    2.2 2.4 2.1 2.1 2.2
    3.1 2.3 2.3 2.3 2.3
    3.2 2.1 2.1 2.1 2.2
    3.3 2.2 2.2 2.2 2.2
    3.4 2.2 2.2 2.1 2.2
    3.5 2.4 2.4 2.4 2.2
  • Flow of control in UCF

    This brings me up to the re-factor. There are distinct stages in ucf's activity:

    1. Parse command line options, and configuration file.
    2. Sanity checks.
    3. Short circuit if purging
    4. Set up debconf
    5. Determine which of the 8 scenarios best fit the configuration file.
    6. Based on the policy flags, determine action to take.

Manoj


Webmaster <webmaster@golden-gryphon.com>
Last commit: Tuesday evening, February 24th, 2009
Last edited Tuesday evening, February 24th, 2009