Creating OpenSolaris Package : Primer
I wanted to create a package for Hudson and contribute that to OpenSolaris. But there's not much information about how to go about creating packages on the OpenSolaris website. This is in stark contrast to projects like Ubuntu, where the packaging is treated as one of the main ways to contribute and the link is easy to find (that said, to be fair, for Hudson's debian packages, I choose to run my own repository for now, so I hadn't contacted them yet. So perhaps it's very difficult process.)
Now, fortunately, I'm a Sun employee, so I do have some contacts and colleagues who are more familiar with Solaris. So through that, I found out an internal website with a lot more information (note to OpenSolaris folks if they are reading this — why is it internal?), which in turn led me to the "SFW consolidation" project of Solaris (I believe SFW stands for "Solaris Freeware Workspace" or something.)
But this turns out to be a nightmare. The very first thing a new package contributer is supposed to do is to check out the SFW workspace, but alas, you can't even do this unless you have TeamWare, which OpenSolaris doesn't have. That's right, you can't create OpenSolaris packages on OpenSolaris out of the box! Even if you manage to check out the workspace, you'll then have to deal with makefiles and 1000s of lines of shell scripts to create SVR4 packages (for the uninitiated, SVR4 packages are the traditional Solaris package format, and the new one in OpenSolaris is called IPS.)
I subsequently learned why they do this rather round about way of creating IPS packages by going through SVR4, but suffice to say that this makes it that much harder to contribute to the packaging effort. I've wasted a better part of a day going through all those scripts, trying to figure out how it works, to no avail. People like Ludo and Jyri in our organization are trying to make this better, but it was still too hard for me. You can also see a post from James Gosling in a related topic, where he complained about the difficulty in making contributions.
But the good news is, there's a better way to do it. If you directly create IPS packages and bypass SVR4 entirely, then it's a much better experience. So the rest of my blog is about how you can leverage all my research and improvements to existing tools, and get up and running with your own OpenSolaris packages quickly, without pulling out your hair.
To introduce you to the art of package making, let me start with a quick intro of IPS.
Many of you must be familiar with a concept of a "repository." In Java context perhaps you know about a Maven repository. Unix users might have heard about Debian package repository, RPM repository, and so on. IPS appears to be conceptually similar to all those well-known repositories, but at the same time it's different in one important way. Namely, there's no package file.
See, when you think of creating a package, you normally think about creating one archive file that contains the stuff. For example, with a Maven repository, you create a jar. With debian repository you create a ".deb" file. Once you have those packages, you put them together in a certain known directory format, generate a bit of metadata by probably running a tool or two, and put them all under Apache, and you are now running a repository.
But it is not so in IPS. With IPS, you first have to have a repository manager called pkg.depotd running. This repository manager does use a file system to store packages, but the storage format is considered its implementation detail and not advertised. And instead of creating a package file, you submit a package by making a series of HTTP requests to this server (which forms a transaction as a whole.) In a way, this is more like the model of version control system --- think Subversion for example. It's defined more in terms of the wire protocol (WebDAV when over HTTP), instead of the storage format. I mean, does anyone know how Subversion revisions are stored on the server?
Personally, I'm not so sure if this is the right approach. For example, my hosting provider for kohsuke.org doesn't have Python for my plan, so I can't run my package repository on my own server. Also, moving packages between repositories, and doing package mirroring, etc., becomes much harder. Or you need to run a repository manager just to test one package that you created. But I guess it's a conscious decision by them, and they are the experts, not me.
In any case, that's about all you need to know about IPS for now, so let's move on.
Creating a package
As I wrote above, creating a package in IPS really means sending a series of HTTP requests to this repository manager. The most well-documented interface for this is pkgsend, and this is actually pretty straight-forward. The following example shows how you can create the "mycmd" package that contains just one file:
// go to one terminal and run a repository manager on port 10000
$ pkg.depotd -p 10000 -d ./my-test-repository
// go to another terminal now
$ eval `pkgsend open firstname.lastname@example.org`
$ pkgsend add file ./mycmd.sh path=/usr/bin/mycmd.sh
$ pkgsend close
You add more stuff by calling "pkgsend add" repeatedly, and you can add files, users, directories, symlinks, and you name it. See this document for more details.
While this does clearly convey how it works, it's pretty horrible way of creating a package because it's too low level (I mean, just imagine your build script or makefile filled with line after line of pkgsend invocation!) And this is where our GlassFish Update Center team does a better job. They've developed a script which directly talks to Python API of IPS (the entire IPS is written in Python), so that you can create a package more productively. I've extended their original work a bit to make it even easier to use (I'm working with them to contribute back those changes.) See this document for more about the script, and you can see an example of what you'd be writing in here (this uses my extension) and here (this doesn't.)
I packaged this tool and published it in Hudson's IPS repository. So if you are using OpenSolaris, you can install my tools as follows:
# pkg set-authority -O http://hudson.gotdns.com/ips/ hudson
# pkg authority
opensolaris.org (preferred) http://pkg.opensolaris.org:80/
# pkg refresh
# pkg install glassfish-makepkgs
DOWNLOAD PKGS FILES XFER (MB)
Completed 1/1 66/66 0.64/0.64
Install Phase 83/83
This creates /usr/bin/makepkgs (symlinked to makepkgs.py), along with a few other tools. With this tool, you now only need to write your small package definition like the ones I linked above, then run it like:
$ makepkgs -d ./build/repo ./proto.py
... and this will deposit a new package into ./build/repo. The tool starts the repository manager temporarily in the same process, so there's no need to run the repository manager separately.
Moving packages around
Most likely, the machine where you'll build a package is different from a machine that hosts the publicly visisble IPS repository. So you'll need some means to transfer packages from one machine to another. For this I wrote the "tarpkgs" tool in the glassfish-makepkgs tool suite. It extracts packages from your repository and bundles them up to a .tgz format for easier transportation.
$ tarpkgs ./build/repo my-package > my-package.tgz
You can then extract this into any other repository. So perhaps you might do something like this to push it to the public repository:
$ tarpkgs ./build/repo my-package | ssh www.kohsuke.org "cd path/to/public/repo; tar xvzf -"
If you wish, you can have more complex set up that involves intermediate repositories for testing. This is a generic command to move packages from one repository to another.
Publishing a package
As I wrote above, to publish a package, you need to be running pkg.depotd , and do so most likely in the read-only mode, or else anyone on the internet would be able to push a package to your repository.
$ /usr/lib/pkg.depotd --readonly -d path/to/repo
This is fairly straight-forward to do in OpenSolaris, which has pkg.depotd out of the box. But chances are, your internet-facing public server doesn't run OpenSolaris. So you'd have to grab all the relevant Python modules and install it on your own (someone in OpenSolaris, please create a tgz for IPS.)
In such an internet facing server, you'll likely be using apache as the front end. If so, see this posting for how to configure pkg.depotd under apache.
You'll be using the tarpkgs script above to deposit the packages you built elsewhere into this repository. When you deploy a new package, however, you have to kill pkg.depotd and restart it, so that it picks up a change. AFAICT from the code, there's no graceful reloading mechanism.
Testing a package
Ideally, you would like to install a package before you publish it, but because IPS always require a server, you first have to publish it before you can install it (very unnatural, if you ask me.) That said, to be fair, you can run pkg.depotd very easily anywhere at any port, so you can at least test it before you push it to a public repository that other people depend.
See the console output above under "creating a package", which shows the steps to hook up your repository and install a new package. If you re-publish your package, you can rerun "pkg refresh" and "pkg install ..." to update to a new package. It's also possible to install a package to a place other than "/" to avoid corrupting your running system.
When you test your own package, you should definitely use ZFS snapshot so that you can get back to the original state before installation. And this is very easy!
take a snapshot before you start messing around
# zfs snapshot rpool/ROOT/opensolaris@before-pkg-installation
and when it's over, roll back!
# zfs rollback rpool/ROOT/opensolaris@before-pkg-installation
In conclusion, creating an IPS package and running your own repository is not really hard, and I hope this guide helps you do that. You just install "glassfish-makepkgs" and off you go.
But at the same time, I have to point out that tooling around IPS is quite weak. The official tool set is virtually non-existent, and available ones have clumsy interfaces. Often error checks are not sufficient, so your mistake often results in the stack dump, and you are on your own to figure out what mistake caused it to choke. I think all these factors are adding up to the lack of community contributions to create packages.
And since not many people are contributing packages, there's no paved road to include your packages into the official repository. I hope Solaris folks would act on these issues before it's too late.
(If there's more interest, I can also talk about how you can install IPS on other Unixes and cross-build packages — in fact I've done all this work on my primary desktop machine that runs Ubuntu.)