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

New packages promise

    XMLWordPrintable

    Details

    • Type: Epic
    • Status: Done
    • Priority: Medium
    • Resolution: Fixed
    • Affects Version/s: None
    • Fix Version/s: None
    • Component/s: Promise type: packages
    • Labels:
      None

      Description

      The promise

      <pre>
      bundle agent main
      {
      packages:
      "firefox" # package name or file path
      policy => "absent/present",

      package_module => apt_get, # Optional, default taken from common/file control

      1. More about package_module below.

      version => "32.0", # Optional, will only match exact version. May be "latest" for present policy type.

      architecture => x86_64";

      1. Optional. Package-module specific and needs to accept anything.

      options =>

      { "--target-release precise" };
      # Options passed directly to the package module. Could be to select repository or special options for dependencies. Or special quirks for package modules like the solaris admin file. Overrides default_options in package_module body

      # additional_packages => { "firefox", "chrome", "opera", "vivaldi" };
      # ^-- Possible way to specify multiple package installs in one promise, for dependency resolution and performance reasons.
      # Might also be a list of absolute file paths if using file-based package management (if the promiser is absolute file path).
      # I propose we keep this out of the first iteration.
      }
      </pre>

      The reason for having file as separate attribute is that it's hard to deduce the
      name that we need to check in the package list without it.

      The package body

      Idea is to have an extremely small package_module body, and put the logic of
      arranging the commands for installation in a dedicated script directory instead,
      like:

      <pre>
      $(sys.workdir)/modules/packages/<package_module>
      </pre>

      The body would only contain ifelapsed values for the different package modules,
      but no reference to commands.

      <pre>
      body package_module apt_get
      {
      query_installed_ifelapsed => "60";
      query_updates_ifelapsed => "1440";
      default_options => { "--target-release precise" }

      ; # overridden by options if present
      }
      </pre>

      Syntax for the package module:

      <pre>
      apt_get supports-api-version
      Stdout:
      1 # The number "one". In case we need to extend the API later.

      apt_get repo-install
      Stdin:
      options=<from-policy-attribute list item 1>
      options=<from-policy-attribute list item 2>
      ...
      Name=firefox
      Version=32-ubuntu # <-- May be omitted
      Architecture=amd64 # <-- May be omitted
      Name=chrome # Each "Name=" denotes a new package entry. Note that current promise spec will only allow one, but this may be extended.
      Version=32-ubuntu
      Architecture=amd64
      ...

      apt_get file-install
      Stdin:
      options=<from-policy-attribute list item 1>
      options=<from-policy-attribute list item 2>
      ...
      File=/media/cdrom/firefox_32-ubuntu1_amd64.deb
      File=/media/cdrom/chrome_32-ubuntu1_amd64.deb
      ...

      apt_get remove
      Stdin:
      options=<from-policy-attribute list item 1>
      options=<from-policy-attribute list item 2>
      ...
      Name=firefox
      Version=32-ubuntu # <-- May be omitted
      Architecture=amd64 # <-- May be omitted
      ...

      apt_get list-installed
      Stdin:
      options=<from-policy-attribute list item 1>
      options=<from-policy-attribute list item 2>
      ...
      Stdout:
      Name=firefox
      Version=32-ubuntu
      Architecture=amd64
      ...

      apt_get list-updates
      Stdin:
      options=<from-policy-attribute list item 1>
      options=<from-policy-attribute list item 2>
      Stdout:
      Name=firefox
      Version=32-ubuntu
      Architecture=amd64
      ...

      apt_get list-updates-local

      1. Exactly same semantics as previous, but does not go online.
        Stdin:
        options=<from-policy-attribute list item 1>
        options=<from-policy-attribute list item 2>
        Stdout:
        Name=firefox
        Version=32-ubuntu
        Architecture=amd64
        ...

      apt_get get-package-data
      Stdin:
      options=<from-policy-attribute list item 1>
      options=<from-policy-attribute list item 2>
      ...
      File=/media/cdrom/firefox_32-ubuntu1_amd64.deb
      Version=32-ubuntu # <-- May be omitted if not provided in policy
      Architecture=amd64 # <-- May be omitted if not provided in policy
      Stdout:
      PackageType=file # Can also be repo. Depends on the type of string passed into the script.
      Name=firefox
      Version=32-ubuntu # <-- May not be returned if the package module does not know.
      Architecture=amd64 # <-- May not be returned if the package module does not know.
      </pre>

      All '...' entries mean that a list is expected (which can have only one element, of course). Otherwise lists are not accepted.

      Error handling will be supported for each and every command except @supports-api-version@. In case of error, script can print to stderr, either:

      <pre>
      File=/path/to/broken/file
      ErrorMessage=File is broken and can not be parsed. # <-- This is optional and may not be returned in case of error.
      </pre>

      ..or:

      <pre>
      Name=broken-package-name
      Version=broken-package-version # If available
      Architecture=broken-package-architecture # If available
      ErrorMessage=File is broken and can not be parsed. # <-- This is optional and may not be returned in case of error.
      </pre>

      ..depending on what the input was. In addition a nonzero error code denotes error, regardless of what was printed to stderr.

      The choice of language for the package module is free, good candidates are of course
      shell or Perl, which should be quite portable, but can also be PowerShell or a
      binary for that matter (the last one is not recommended). But the key point is that it
      is an actual programming language where we can have decision logic, and preferably a
      scripted one.

      The attribute defining which package module is used for inventory query.

      <pre>
      body common control
      {
      bundlesequence =>

      { ... };
      inputs => { ... }

      ;

      package_inventory =>

      { $(package_module_knowledge.platform_default) }

      ;
      package_module => $(package_module_knowledge.platform_default);
      }

      in masterfiles/lib/3.7/packages.cf
      bundle common package_module_knowledge
      {
      vars:
      debian::
      "platform_default" string => "apt_get";

      redhat::
      "platform_default" string => "yum";
      ...
      }
      </pre>

      If the exact package module is not specified in the promise itself the package_module in body common control is chosen. In other words the order in which the package_module script will be chosen is as follows: promise definition, body common control. If neither is set, an error is generated; preferably at parse-time.

      Requirements for implementation

      • All installations/removals/updates must trigger an inventory update to prove that the operation was successful, since most package managers are bad at reporting this, and this should be used in the promise result.
      • If a cf-agent is calling a package module, a second cf-agent should not call it, in order to avoid cascading cf-agents when the package manager hangs. This happens regularly when the rpm db is corrupted or there is a slow network operation.

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                a10003 Eystein Maloy Stenberg
                Reporter:
                a10003 Eystein Maloy Stenberg
              • Votes:
                0 Vote for this issue
                Watchers:
                8 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved:

                  Summary Panel

                    Time Tracking

                    Estimated:
                    Original Estimate - 4 weeks, 7 hours
                    4w 7h
                    Remaining:
                    Not Specified
                    Logged:
                    Time Not Required
                    Not Specified