Uploaded image for project: 'CFEngine Community'
  1. CFEngine Community
  2. CFE-2959

Installing "latest" via yum package module using "enablerepo" option doesn't work

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Open
    • Priority: (None)
    • Resolution: Unresolved
    • Affects Version/s: None
    • Fix Version/s: None
    • Component/s: cf-agent
    • Labels:
      None

      Description

      The following promise does not work as you would expect:

        packages:
          redhat::
            "some-package"
              options => { "enablerepo=UPDATES" },
              comment => "UPDATES repo is disabled in /etc/yum.repos.d/ so we'll enable it for this one install",
              handle => "promise_for_latest",
              policy => "present",
              version => "latest";
      

      This is counterintuitive.

      Each individual component is working as designed. However, the overall design isn't capable of dealing with this use case.

      The above promise will have the following behavior. All of this is "expected" behavior; some of it is undesirable behavior:

      1. Happens and is desirable: If "some-package" isn't installed at all, it will be updated to the latest available from the UPDATES repo.
      2. Happens and is desirable: If "some-package" is installed, and there is a later version available from an already enabled repo and an even later version available from the UPDATES repo, the latest version (from the UPDATES repo) will be installed.
      3. Happens and is UNDESIRABLE: If "some-package" is installed, and the usual repos don't have any update available for it, it will not be updated even when the UPDATES repo has an available update.

      The reason for this is simple: CFEngine caches its list of available software updates as part of system discovery. This system discovery does not use the "enablerepo" option.


      My conclusion based on this and other data is that the way that package module bodies integrate with package modules, and the way that "default_options" are handled in package module bodies, needs to be redesigned.

      The current design handles many use cases well but has inherent limitations. Improvements to handle these limitations could probably be made by extending the package module API and calling the result "v2" of the package module API.

      However, in this issue I will just lay out this specific bug; proposals for a redesign (and the reasons I think it's needed) will be laid out separately as this bug is only one of reasons.


      I'll document the workarounds I'm aware of, and the shortcomings they have.

      CFE-2103 is relevant here. Since some of the points in that ticket have been dealt with in 3.13.0, there is a new workaround possible in 3.13.0 (it's #5 below), but it's still not a clean workaround.

      Workaround #1 - just specify the version explicitly.

      If you know what specific version you expect to have available from the UPDATES repo, this might work for you. However, it doesn't solve the exact use case of the promise above. It does something else. If you have packages published to your UPDATES repo more often or on a different schedule than you publish CFEngine policy, and you want exactly what the promise above states (the latest version from the UPDATES repo), this workaround is not useful.

      Workaround #2 - use a commands promise.

      Of course you can bypass the entirety of CFEngine's package management capabilities and just write a commands promise directly that will call yum with the options and arguments you want. You'll have to write your own detection code, too, to determine if you need to execute the command. If you're going with this workaround it's an indictment of CFEngine's package management capabilities.

      Workaround #3 - promise the removal of the old version(s) that's available from the standard repos.

      If you know the version that will be present from repos other than your UPDATES repo, and if the removal of that version won't remove other necessary packages due to dependencies, you can add an additional promise to remove that specific version:

        packages:
          redhat::
            "some-package"
              comment => "We want the shiny new version from the UPDATES repo but we have to remove this version for CFEngine to notice an update is available",
              policy => "absent",
              version => "0.1-old-version";
      

      This will be another promise in addition to the "promise_for_latest" at the top of this ticket. This promise will remove the old version, and then CFEngine will notice the old version as an available update, so it will execute the "promise_for_latest" and thereby get the new version from the UPDATES repo.

      Make sure this comes before the "promise_for_latest" above, or the package will remain uninstalled until the next agent run.

      If you have a large and varied infrastructure with many old versions in play from different repositories, you'll have to add an "absence" promise for each one of these versions and keep an eye out for any new "old versions" turning up that you haven't accounted for. This approach is relatively workable, but it's kludgy and you can't be confident that your code will converge as expected.

      Workaround #4 - add the "enablerepo" flag in the default_options attribute for the yum package module body.

      This works but has side effects. Essentially it works the same from CFEngine's perspective as just modifying the repo file itself to be enabled: all packages may be installed/updated from the UPDATES repo for any packages promise; "available updates" inventory will be based on the UPDATES repo; you'll have to pass an explicit "disablerepo" flag for any promise where you want to avoid the UPDATES repo; etc.

      (Using the "disablerepo" flag for a "latest" packages promise has its own problems. This ticket describes how using the "enablerepo" option can result in the promise never being evaluated or repaired when it should be; well, "disablerepo" option with a "latest" version can cause the counterpart issue of that, such that the promise will have a repair attempt every single agent run without any succeeding.)

      Workaround #5 - (only possible in 3.13.0 or higher) make a copy of the yum package module body with the default_options set to enable UPDATES.

      Technically this is possible below 3.13.0 if you copy the entire package module (the python script located at /var/cfengine/modules/packages/yum) and give it a new name and make a new package module body with a matching name but the default_options set differently.

      In either case, this approach has the drawback that CFEngine will regard packages installed using one of these package module bodies as completely separate from packages installed with the other.

      You will have two separate caches for available updates, one per package module body. (Only one of the two lists will appear in available updates inventory, though, as determined by the package_inventory attribute in body common control.) You will also have two separate caches for installed packages, even though these lists should be identical. This is extra overhead and also opens the door to inconsistency between the cached lists of installed packages.


      I don't know of any other workarounds.

        Attachments

          Activity

            People

            • Assignee:
              Unassigned
              Reporter:
              mweilgart Mike Weilgart
            • Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

              Dates

              • Created:
                Updated:

                Summary Panel