ptomulik/puppet-portsng

View on GitHub
README.md

Summary

Maintainability
Test Coverage
# ptomulik-portsng
[![Puppet Forge](https://img.shields.io/puppetforge/v/ptomulik/portsng.svg)](https://forge.puppet.com/ptomulik/portsng)
[![Build Status](https://travis-ci.org/ptomulik/puppet-portsng.png?branch=master)](https://travis-ci.org/ptomulik/puppet-portsng)
[![Coverage Status](https://coveralls.io/repos/ptomulik/puppet-portsng/badge.png?branch=master)](https://coveralls.io/r/ptomulik/puppet-portsng?branch=master)
[![Code Climate](https://codeclimate.com/github/ptomulik/puppet-portsng/badges/gpa.svg)](https://codeclimate.com/github/ptomulik/puppet-portsng)

## <a id="table-of-contents"></a>Table of Contents

1. [Overview](#overview)
2. [Module Description](#module-description)
3. [Setup](#setup)
    * [What portsng affects](#what-portsng-affects)
    * [Setup requirements](#setup-requirements)
    * [Beginning with portsng](#beginning-with-portsng)
4. [Troubleshooting](#troubleshooting)
5. [Limitations](#limitations)
6. [Development](#development)

## <a id="overview"></a>Overview

This is an enhanced __ports__ provider for package resource (FreeBSD).
[FreeBSD Ports and Packages Collection](https://www.freebsd.org/ports/)
offers a simple way for users and administrators to install applications.
This provider enables puppet to manage FreeBSD ports on agent OS.

The module requires ``port-maintenance-tools`` to be installed on agent.

## <a id="module-description"></a>Module Description

The module is an alternative to puppet's built-in __ports__ provider.
It provides additional features and is free of several issues found in the
built-in __ports__ provider. For details see [remarks](#remarks).

\[[Table of Contents](#table-of-contents)\]

## <a id="setup"></a>Setup

### <a id="what-portsng-affects"></a>What portsng affects

* installs, upgrades, reinstalls and uninstalls packages,
* modifies FreeBSD ports options' files `/var/db/ports/*/options.local` or
  `/var/db/ports/*/options` (if really outdated ports tree is used).

\[[Table of Contents](#table-of-contents)\]

### <a id="setup-requirements"></a>Setup Requirements

You may need to enable __pluginsync__ in your `puppet.conf`.

\[[Table of Contents](#table-of-contents)\]

### <a id="beginning-with-portsng"></a>Beginning with portsng

Its usage is essentially same as for the original *ports* provider. Just select
*portsng* as the package provider

```puppet
Package { provider => portsng }
```

Below I just put some examples specific to new features of *portsng*.

#### <a id="example-1---using-package_settings"></a>Example 1 - using *package_settings*

Use *package_settings* to ensure that appropriate compilation options are set
for a port. Normally (without puppet) you would set these with ``make config``
command. In the following example we ensure that ``www/apache22`` is installed
with ``SUEXEC`` enabled:

```puppet
package { 'www/apache22':
  package_settings => {'SUEXEC' => true}
}
```

\[[Table of Contents](#table-of-contents)\]

#### <a id="example-2---using-uninstall_options-to-cope-with-dependency-problems"></a> Example 2 - using *uninstall_options* to cope with dependency problems

Sometimes, the FreeBSD package manager refuses to uninstall a package if there
are other packages installed that depend on this one. In such situations we may
use *uninstall_options* to (recursively) uninstall all the packages dependant
on the one being uninstalled. If [pkgng](http://www.freebsd.org/doc/handbook/pkgng-intro.html)
is used on FreeBSD as a package manager (default since 10.3), one has to write:

```puppet
package { 'www/apache22':
  ensure => absent,
  uninstall_options => ['-R','-y']
}
```

When still using ports with ancient
[pkg](https://docs.freebsd.org/doc/9.0-RELEASE/usr/share/doc/freebsd/en/books/handbook/packages-using.html)
package manager one would write in its manifest:

```puppet
package { 'www/apache22':
  ensure => absent,
  uninstall_options => ['-r']
}
```

\[[Table of Contents](#table-of-contents)\]

#### <a id="example-3---using-install_options"></a>Example 3 - using *install_options*

The new *portsng* provider implements *install_options* feature. The flags
provided via *install_options* are passed to [portupgrade](https://www.freebsd.org/cgi/man.cgi?query=portupgrade)
command when installing, reinstalling or upgrading packages. With no
*install_options* provided, sensible defaults are selected by *portsng* provider.

Let's say we want to install precompiled package, if available (`-P` flag).
Write the following manifest:

```puppet
package { 'www/apache22':
  ensure => present,
  install_options => ['-P', '-M', {'BATCH' => 'yes'}]
}
```

Now, if we run puppet, we'll see the command:

```console
~ # puppet agent -t --debug --trace
...
Debug: Executing '/usr/local/sbin/portupgrade -N -P -M BATCH=yes www/apache22'
...
```

Note, that the *portsng* provider adds some flags by its own (`-N` in the above
example). What is added/removed is preciselly stated in provider's generated
documentation.

\[[Table of Contents](#table-of-contents)\]

## <a id="troubleshooting"></a>Troubleshooting

* puppet is unable to find information about not yet installed ports

  - try to run manually (as root)

    ```console
    ~ # make seach -C /usr/ports/misc/figlet
    ```

    if it prints something like "Please run make index", then follow that
    advice. You may also check ``/usr/ports`` for ``INDEX-*`` files and if
    they're absent, then regenerate index with ``make index``. Note, that
    it may take a while.

* portupgrade hangs when called from puppet (on some older FreeBSD versions)

  - it may be the [script(1)](https://www.freebsd.org/cgi/man.cgi?script%281%29)
    command hanging. You may try to run the following script to fix pkgtools

    ```console
    #!/bin/sh

    set -e

    # Fix for hanging "script -qa ... " in pkgtools.rb used by portupgrade
    if [ -d '/usr/local/lib/ruby' ]; then
      echo "/usr/local/lib/ruby is a directory";
      for F in `find /usr/local/lib/ruby -name 'pkgtools.rb' -type f`; do
        UNCHMOD=false;
        echo "patching $F...";
        test -w $F || (chmod u+w $F; UNCHMOD=true);
        sed -e "s/\[script_path(), '-qa', file, \*args\]/[script_path(), '-t', '0', '-qa', file, \*args]/" \
            -e "s/\['\/usr\/bin\/script', '-qa', file, \*args\]/['\/usr\/bin\/script', '-t', '0', '-qa', file, \*args]/" \
            -i '' $F;
        if $UNCHMOD; then chmod u-w $F; fi
      done
    fi
    ```

    The script may also be [downloaded from github](https://raw.githubusercontent.com/ptomulik/puppet-portsng/master/.fixes/fix-hanging-pkgtools.sh)

* installation fails with error message

  ```console
  /usr/local/sbin/portupgrade:569:in `chdir': HOME/LOGDIR not set (ArgumentError)
  ```

  - this is caused by portupgrade v. 2.4.10.X, see
    [FreeBSD Bug #175281](https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=175281),
  - install another version of portupgrade to fix this.

\[[Table of Contents](#table-of-contents)\]

## <a id="limitations"></a>Limitations

* If there are several ports installed with same *portname* - for example
  `docbook` - then `puppet resource package docbook` will list only one of
  them (the last one from `portversion`s list - usually the most recent). It is
  so, because `portsng` uses *portorigins* to identify its instances (as `name`
  paramateter). None of the existing `instances` is identified by `puppet` as
  an instance of `docbook` and `puppet` falls back to use provider's `query`
  method. But `query` handles only one package per name (in this case the last
  one from *portversion*'s list if chosen). This is an issue, which will not
  probably be fixed, so you're encouraged to use *portorigins*.

## <a id="remarks"></a>Remarks

### <a id="how-it-corresponds"></a> How it corresponds to the puppet's built-in __ports__ provider

Main motivation for this module being developed was that the puppet's
built-in __ports__ provider had several defects and missing features at time of
this writing. By implementing it from scratch, I hoped (and, I think, managed)
to provide all these missing features and to release a module free of all the
bugs (and misconceptions) I found in the built-in one. So below is short
description of what was achieved.

The new features include:

  * *install_options* - extra CLI flags passed to
    [portupgrade(1)](https://www.freebsd.org/cgi/man.cgi?query=portupgrade)
    when installing, reinstalling and upgrading packages,
  * *uninstall_options* - extra CLI flags passed to
    [pkg_deinstall(1)](https://www.freebsd.org/cgi/man.cgi?query=pkg_deinstall&sektion=1)
    (the ancient [pkg](https://docs.freebsd.org/doc/9.0-RELEASE/usr/share/doc/freebsd/en/books/handbook/packages-using.html)
    toolstack) or [pkg delete](https://www.freebsd.org/cgi/man.cgi?query=pkg&sektion=8)
    ([pkgng](http://www.freebsd.org/doc/handbook/pkgng-intro.html)) when
    uninstalling packages,
  * *package_settings* - configuration options for package, the ones you
    usually set with ``make config``,
  * works wit both the ancient
    [pkg](https://docs.freebsd.org/doc/9.0-RELEASE/usr/share/doc/freebsd/en/books/handbook/packages-using.html) and new
    [pkgng](http://www.freebsd.org/doc/handbook/pkgng-intro.html) package
    databases,
  * *upgradeable* (tested, the original puppet provider declared that it's
    upgradeable, but it never worked for me),
  * *portorigins* (instead of *portnames*) are used internally to identify
    package instances,
  * [portversion](http://www.freebsd.org/cgi/man.cgi?query=portversion&manpath=ports&sektion=1)
    is used to find installed packages (instead of *pkg_info*),
  * [make search](http://www.freebsd.org/cgi/man.cgi?query=ports&sektion=7) is
    used to find (not-installed) ports listed in puppet manifests,
  * several issues resolved,

The *package_settings* is simply an `{OPTION => value}` hash, with boolean
values. The *portsng* provider ensures that package is compiled with prescribed
*package_settings*. Normally you would set these options with *make config*
command using ncurses-based frontend. Here, you can define *package_settings*
in your puppet manifest. If a package is already installed and you change its
*package_settings* in manifest file, the package gets rebuilt with new options
and reinstalled.

Instead of *portnames*, *portorigins* are used to identify *portsng* instances
(see [FreeBSD ports collection and it's
terminology](#freebsd-ports-collection-and-its-terminology)). This copes with
several problems caused by portnames' ambiguity (see [FreeBSD ports collection
and ambiguity of
portnames](#freebsd-ports-collection-and-ambiguity-of-portnames)). You can now
install and mainain ports that have common *portname* (but different
portorigins). Examples of such packages include *mysql-client* or *ruby* (see
below).

The [portversion](http://www.freebsd.org/cgi/man.cgi?query=portversion&manpath=ports&sektion=1)
utility is used to find installed ports. It's better than using
[pkg_info](http://www.freebsd.org/cgi/man.cgi?query=pkg_info&sektion=1) for
several reasons. First, it is said to be faster, because it uses compiled
version of ports INDEX file. Second, it works with both - the ancient
[pkg](https://docs.freebsd.org/doc/9.0-RELEASE/usr/share/doc/freebsd/en/books/handbook/packages-using.html) database and the
new [pkgng](http://www.freebsd.org/doc/handbook/pkgng-intro.html) database,
providing seamless interface to any of them. Third, it provides package names
and their "out-of-date" statuses in a single call, so we don't need to
separatelly check out-of-date status for installed packages. This version of
*portsng* works with old *pkg* database as well as with *pkgng*, using
*portversion*.

\[[Table of Contents](#table-of-contents)\]

#### <a id="freebsd-ports-collection-and-its-terminology"></a>FreeBSD ports collection and its terminology

We use the following terminology when referring ports/packages:

  * a string in form `'apache22'` or `'ruby'` is referred to as *portname*
  * a string in form `'apache22-2.2.25'` or `'ruby-1.8.7.371,1'` is referred to
    as a *pkgname*
  * a string in form `'www/apache22'` or `'lang/ruby18'` is referred to as a
    port *origin* or *portorigin*

See [http://www.freebsd.org/doc/en/books/porters-handbook/makefile-naming.html](http://www.freebsd.org/doc/en/books/porters-handbook/makefile-naming.html)

Port *origins* are used as primary identifiers for *portsng* instances. It's recommended to use *portorigins* instead of *portnames* as package names in manifest files.

\[[Table of Contents](#table-of-contents)\]

#### <a id="freebsd-ports-collection-and-ambiguity-of-portnames"></a>FreeBSD ports collection and ambiguity of portnames

Using *portnames* (e.g. `apache22`) as package names in manifests is allowed.
The *portname*s, however, are ambiguous, meaning that port search may find
multiple ports matching the given *portname*. For example `'mysql-client'`
package has three ports at the time of this writing  (2013-11-30):
`mysql-client-5.1.71`, `mysql-client-5.5.33`, and `mysql-client-5.6.13` with
origins `databases/mysql51-client`, `databases/mysql55-client` and
`databases/mysql56-client` respectively. If none of these ports are installed
and you use this ambiguous *portname* in your manifest, you'll se the following
warning:

```console
Warning: Puppet::Type::Package::ProviderPorts: Found 3 ports named 'mysql-client': 'databases/mysql51-client', 'databases/mysql55-client', 'databases/mysql56-client'. Only 'databases/mysql56-client' will be ensured.
```

\[[Table of Contents](#table-of-contents)\]

## <a id="development"></a>Development
The project is held at github:
* [https://github.com/ptomulik/puppet-portsng](https://github.com/ptomulik/puppet-portsng)
Issue reports, patches, pull requests are welcome!