Munki trials with a local repository

While I was working on how to compare OS X versions using only strings and create a Munki alert with just one button, I tested with a local Munki repository. It let me perform tests without affecting a “real” Munki repo.

To set this up, I first changed the Munki SoftwareRepoURL preference so that it used a “file” URL:

    defaults write SoftwareRepoURL "file:///Users/jeremy/Documents/munki_repo"

I then created that folder and its subfolders.

    mkdir -p ~/Documents/munki_repo/{catalogs,manifests,pkgsinfo}

I added the “nopkg” pkginfo for the alert testing by creating it from the command line. I piped the makepkginfo output the BBEdit, edited the pkginfo there, and saved the resulting file to the “pkgsinfo” folder.

    makepkginfo --nopkg --name="ok_cancel_alert" | bbedit

To test the conditional version string comparisons, I created and populated a property list in the “manifests” folder. I initially tried to use the “site_default” manifest name, since I thought that it would be the most generic — and thus useful — idea.

It wasn’t working, though. I had to specify the name of a manifest that matched the hostname of the computer. I was thinking that there was some reliance on DNS, but I eventually realized that I’d set the ClientIdentifier preference.

    sudo defaults delete /Library/Preferences/ManagedInstalls.plist ClientIdentifier

Once I had removed this preference, the client was able to fall through to the generic “site_default” manifest. That was exactly what I wanted. The “managedsoftwareupdate” tool was able to try for various manifest names using the standard ordered list of identifiers before it finally settled on “site_default” one.

    Request failed. Trying site_default...
    Getting manifest site_default...
    Using manifest: site_default

Once that was sorted out, I was able to create pkgsinfo and manifests to use in local testing. I could also remove any pkgsinfo that were no longer needed for testing. I still had to remember to “makecatalogs” for any pkginfo changes to take effect.

    makecatalogs ~/Documents/munki_repo

This technique was definitely useful to me for testing on a local system. It didn’t affect any other network-based Munki repository and required minimal setup, starting with a preference change on the client. I would also expect it to work locally within virtual machines that are used for testing. The Munki repository in a local folder also allows it to be used offline, should the need arise for that.

This is where we used to live

I know I don’t live there anymore — and haven’t lived there for a long time — but I feel compelled to note that my parents finally sold their home on Cuba Lake.

We visited at the beginning of the month and had a great time with the family. We’ll have to make some new traditions to get together in some other place.

Cuba felt like the same place, in many ways, but very different in others — if for no other reason than I’m no longer who I was when I was growing up there, and many friends that I knew have moved elsewhere.

I’ll miss it, in the same wistful sense that I miss other things that have ended, or that I might never see again. It feels like we’ve built up quite a few of those cherished but fading memories in a short time, thanks in no small part to our own move to Pennsylvania.

Version comparison by string with JAMF JSS Smart Groups

JAMF JSS can create Smart Groups, which are similar to Smart Playlists in iTunes or Smart Folders in the Finder. When it comes to string comparisons, the following search operators are available: “is,” “is not,” “like,” and “not like.”

OS X version comparison using strings (the introduction to this series) works with JAMF JSS. Searches with the “like” and “not like” operators do not anchor on periods, in my experience. This switches to the second kind of comparison I described in the pseudocode.

  1. Create the “OS X v10.8 and newer” smart group.
  2. Add the following criteria to the group. The default “and” is used to join them.
    • “Operating system” “like” “10”
    • “Operating system” “not like” “10.3”
    • “Operating system” “not like” “10.4”
    • “Operating system” “not like” “10.5”
    • “Operating system” “not like” “10.6”
    • “Operating system” “not like” “10.7”
  3. Save the smart group.

Create the first one, then use the JSS’ “Clone” function, to make additional comparisons for other OS X versions. Edit the copies as needed.

No ongoing maintenance of older Smart Groups should be required. I had previously up with a scheme that used Smart Groups relying on other Smart Groups. It required one update to the prior “and greater” group for each new OS X release. The maintenance effort can be avoided with the technique above and both produce the same effect.

Version comparison by string with Munki conditionals

Munki can use Python’s version comparison tools to perform mathematical version comparisons for the minimum and maximum OS versions and minimum Munki version specified in pkgsinfo files.

In manifest files’ conditional items, though, only string comparisons are available. The OS X version comparison using strings (the introduction to this series) technique can be useful with the “os_vers” attribute in manifests.

Conditional items use NSPredicates support for regular expressions. These searches can anchor on periods.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>catalogs</key>
    <array>
        <string>production</string>
    </array>
    <key>conditional_items</key>
    <array>
        <dict>
            <key>condition</key>
            <string>os_vers BEGINSWITH "10." AND NOT (os_vers BEGINSWITH "10.0." OR os_vers BEGINSWITH "10.1." OR os_vers BEGINSWITH "10.2." OR os_vers BEGINSWITH "10.3." OR os_vers BEGINSWITH "10.4." OR os_vers BEGINSWITH "10.5." OR os_vers BEGINSWITH "10.6." OR os_vers BEGINSWITH "10.7.")</string>
            <key>managed_installs</key>
            <array/>
            <key>managed_uninstalls</key>
            <array/>
            <key>managed_updates</key>
            <array/>
            <key>optional_installs</key>
            <array/>
        </dict>
    </array>
</dict>
</plist>

Version comparison by string with UNIX shell

The OS X version comparison using strings (the introduction to this series) can be implemented with a UNIX shell script. I figured the most direct method was to use grep. I also like case statements.

These searches can anchor on periods.

#!/bin/bash

# Get the current OS version to serve as an example
THIS_OS=$(sw_vers -productVersion)

# Determine if the OS version starts with "10."
echo "${THIS_OS}" | grep -q -e ‘^10.’

case $? in
    # If the OS version matches the initial basic criteria,
    # then determine if it is greater than a specific OS X version
    # by string comparison against lower major version numbers.
    0)
        # Eliminate each major OS X version, one by one, with string matching.
        # Stop with the version before the one of interest.
        echo "${THIS_OS}" | \
            grep -v -q \
                -e ‘^10.0.’ \
                -e ‘^10.1.’ \
                -e ‘^10.2.’ \
                -e ‘^10.3.’ \
                -e ‘^10.4.’ \
                -e ‘^10.5.’ \
                -e ‘^10.6.’ \
                -e ‘^10.7.’ && \
                echo "OS X ${THIS_OS} is version OS X 10.9 or greater."
        ;;
    *)
        echo "OS X version number not found."
        ;;
esac

Version comparison solution pseudocode

This OS X version comparison solution (the introduction to this series) takes some setup for each version comparison.

Once created, the comparison can be copied or cloned to form the basis for the next comparison. The duplicate copy can be edited to create new clauses for another comparison (adding “is this OS X 10.9 or later?” and “is this OS X 10.10 or later?” when needed). It also accommodates new OS X versions without any changes, assuming that the version numbering scheme continues with either 10.x (or increments above 10.x to 11.x and beyond).

The pseudocode to answer the “is this OS X 10.8 or later?” question could be expressed this way:

  • Does the version start with OS X “10.”?
    • Does the version not start with OS X “10.0.”?
    • Does the version not start with OS X “10.1.”?
    • Does the version not start with OS X “10.2.”?
    • Does the version not start with OS X “10.3.”?
    • Does the version not start with OS X “10.4.”?
    • Does the version not start with OS X “10.5.”?
    • Does the version not start with OS X “10.6.”?
    • Does the version not start with OS X “10.7.”?

If the result of evaluating those statements is true, then the OS X version is 10.8 or greater.

Note the periods: This technique works as long as the periods can be evaluated, to serve as anchors between the numbers.

In some circumstances, the periods cannot be used as anchors. In cases like that, I have dropped off checking for OS X 10.0, 10.1, and 10.2. I expect that those are the three versions most likely to conflict with any further OS X releases in the 10.x series.

  • Is the version like OS X “10”?
    • Is the version not like OS X “10.3”?
    • Is the version not like OS X “10.4”?
    • Is the version not like OS X “10.5”?
    • Is the version not like OS X “10.6”?
    • Is the version not like OS X “10.7”?

These techniques can be applied in a few different environments.

Compare OS X versions using only strings

There are many system administration tasks in OS X that require version comparisons. Looking back over my time managing OS X, I’m surprised that this still presents some complexities in 2015. This article is the first in a short series on this topic and presents a solution to one of the common problems.

I have found that the most robust tools for these comparisons are available in the Python Standard Library, which makes them available up through at least OS X 10.10 Yosemite. The modules in Python have the advantage of treating versions more like numbers — so greater than, less than, and equal comparisons are possible.

With Python, I believe we have at least two good choices of libraries that handle versions as versions.

  • I wrote about “distutils.version” just over eight years ago, after talking about the problem with my friend Steve. It offers two classes, StrictVersion and LooseVersion, that are each helpful in their own way. It is probably more popular on OS X, by the shear volume of everyday usage.

  • My friend Nate and I included “pkg_resources.parse_version” in our Penn State Mac Admins Conference 2013 talk, “Practical Python for Sysadmins” (slides 50 and 52). That module does the job and sounded good in PEP 386. It presents an alternative to “distutils.version” if you want one.

Many system management tools resort to string comparison. Many scripts break down strings for numeric comparisons of components. These two techniques can accomplish what needs to be done, but have they have limits.

Especially when it is all that’s available, string comparison feels very limited. I have struggled trying to figure out how to handle “greater than” or “greater than or equal to” comparisons within those confines. In my own work, that particular comparison is a common, recurring need. There are many instances when I need to answer a basic question like “is this OS X 10.8 or later?” In number line terms:

NumberLineOSX108OrGreater.png

With some trial and error, I now have a way that appears to work reliably. I realized it could apply generally to different tools. I don’t know if it’s novel, since I haven’t found any references elsewhere to a similar technique. (If you know of one, I’d like to link to it.)

Find out more about this.

Create a Munki alert with just one button

I found that there is a way to get only one button in a Munki preinstall alert. By default, there are two, one for “OK” and the other for “Cancel.”

  1. Override the “ok label” with a blank string.
  2. Set the “cancel label” to the “OK” string.

The alert dictionary looks like this:

        <key>preinstall_alert</key>
        <dict>
            <key>alert_title</key>
            <string>Alert</string>
            <key>alert_detail</key>
            <string>This is an alert that you should cancel, even if the button label is "OK."</string>
            <key>ok_label</key>
            <string></string>
            <key>cancel_label</key>
            <string>OK</string>
        </dict>

It results in a dialog with a single, default button labelled “OK,” instead of two buttons. It looks like this:

MunkiMSC_optional_alert_singlebutton_02.png

It wasn’t immediately obvious to me that setting a blank label for one of the alert buttons would make that button disappear, but I’m glad it did.

Update: This could be used for an optional deployment with conditions. It provide an indication that an installation is available but conditions need to be met.

  1. Provide the real deployment when specific conditions are met.
  2. When those conditions aren’t met, provide a simple alternate deployment — a different pkginfo with the same display name and icon — that will present the alert with a single button to describe how to meet the conditions. When the user clicks “OK,” the alternate deployment is cancelled. No harm, no foul. Using a simple install check script (“exit 0”), the alternate deployment will keep appearing until the conditions are met and the real deployment is in scope.

Archive OS X Installer application workflow

I created an automated workflow to help me archive the OS X Installer application when I download updates from the Mac App Store. The release of OS X Yosemite and all these inadequately-labeled installer apps I have lying around at home spurred me on.

Each archived installer is saved as a disk image to /Users/Shared and named with its OS X version number and build.

I thought that it might be of general interest, so you can see more at Bitbucket or Github. The workflow is automated courtesy of Noodlesoft Hazel.

In a whole different Size Class

The march towards responsive design — now with Size Classes — is a watershed moment for iOS. I can’t help but think that some of the moves Apple has made — and might make at its October 2014 announcement tomorrow — are centered around screens. This is all speculation on my part, but it’s been rolling around my head for a while now.

What if there’s no 5-inch iPhone 6 because Apple wants to move developers to update for Size Classes? No longer having the same iPhone screen as last year in the flagship phones is strong encouragement; both the iPhone 6 and 6 Plus demand that apps be updated. The big screens are a marquee feature of both new phones. Adding Notification Center interfaces is probably another part of this.

I could certainly see more encouragement coming from an expansion of the iPad line, adding a larger model alongside the existing 8 and 10 inch devices. Split screen display of multiple apps? More encouragement.

My conceit for some time has been that the establishment of the iPad Mini would eventually free Apple to increase the resolution and screen density of the iPad Air. There’s not necessarily a reason for the Mini and the Air to share the same resolution. Bumping up the resolution and density on the Air this year would give one more reason to throw out the pixel-perfect designs of the past. (I just hope the GPUs can handle such a move.)

Mac developers

Syndicate content