Habitat Package Refresh 2021

Hello, friendly Habicats!

We will be refreshing the Habitat Packages (Base Plans and Core Plans) on Monday, September 13. Let’s go over what this means for us and, more important, what it means for you.

Habitat Plans 101

Currently, there are about 700 Habitat plans under the Core Origin on Builder.

These include everything from services like Postgresql to binaries like Go to very low level system libraries like GCC, all of which you can easily pull into your own Habitat artifacts.

What’s a Habitat Plan

Artifacts are the cryptographically-signed tarballs that are uploaded, downloaded, unpacked, and installed in Chef Habitat. They are built from shell scripts known as plans, but may also include application lifecycle hooks and service configuration files that describe the behavior and configuration of a running service.
At the center of Chef Habitat packaging is the plan. This is a directory comprised of shell scripts and optional configuration files that define how you download, configure, make, install, and manage the lifecycle of the software in the artifact
The Base Plans are a group of plans (mostly low level systems libraries) that:

  • are used by nearly every other plan (or a dependency of that plan) on Builder

  • are used to build Habitat itself

  • need to be built in a certain order.

You can see the full list of Base Plans here.
You can see the full list of Core plans here.

Any time one of these plans is updated (especially ones that are nearly universally used, like glibc and GCC, all other plans which depend on it need to be built in a certain order, and then all plans that depend (or depend on a plan that depends) on those plans also need to be rebuilt.

Why are you refreshing them?

We perform periodic habitat package (Core Plans/Base Plans) refreshes to ensure that your applications built on Habitat are secure and performant. Many packages including GCC, OpenSSL, zlib, and glibc form the base cryptographic and security libraries that applications depend on.

We also perform periodic plan refreshes to make it easy for users of Habitat to receive the latest performance improvements, security patches, and bug fixes from the underlying software libraries that power your application. Our periodic plan refresh means you will receive these updates automatically, and you will never worry about your application being tied to an aging Operating System version.

This habitat package refresh upgrades GCC to version 9.3.0. (GCC 9 Release Series — Changes, New Features, and Fixes - GNU Project), glibc to version 2.33 (The GNU C Library version 2.33 is now available), and nginx to 1.21.0 which fixes a critical vulnerability (https://nginx.org).

Plans and You

So far we’ve discussed how and why we are refreshing the Habitat Packages, but what does that mean for the plans in your own origin?

How does this affect me?

Chances are, any plan within your origin depends on at least one of the Habitat Packages ( Base Plans /Core Plans) somewhere in its chain of dependencies. When that plan is updated, your plan also must be rebuilt. This sounds simple, but if you have plans in your origin that depend on other plans – and those plans depend on a Base Plan (let’s say glibc), all of them must be built with the same version of glibc. Let’s look at this visually.

Dependencies

Let’s say I have a plan under my origin (nellshamrell) called widget_world – let’s say it lives at

nellshamrell/widget_world on Builder.
nellshamrell/widget_world

Now, let’s say that plan depends on two more plans, nellshamrell/widget and nellshamrell/world (yes, it’s contrived, just go with it for now).

nellshamrell/widget_world
-> nellshamrell/widget
-> nellshamrell/world

And let’s say each of those plans depends on core/glibc for some reason:

nellshamrell/widget_world
-> nellshamrell/widget
--> core/glibc
-> nellshamrell/world
--> core/glibc

And let’s say both are using the current core/glibc plan, which means they are both using version 2.29:

nellshamrell/widget_world
-> nellshamrell/widget
--> core/glibc/2.29
-> nellshamrell/world
--> core/glibc/2.29

My nellshamrell/widget_world plan will build fine, because both of its dependencies are built with the same version of glibc.

(studio) $ build widget_world
...
Success!
I love when a plan.sh comes together!

Then suppose the Habitat team upgrades core/glibc to 2.33. And let’s say for some reason you only update one of the dependencies to use the updated glibc – so nellshamrell/widget will depend on core/glibc/2.33, while nellshamrell/world will still depend on core/glibc/2.29:

nellshamrell/widget_world
-> nellshamrell/widget
--> core/glibc/2.33
-> nellshamrell/world
--> core/glibc/2.29

This time, if I attempt to build nellshamrell/widget_world, it will return a very ugly error that looks similar to this:

(studio) $ build widget_world
(...)
widget_world: WARN
widget_world: WARN The following runtime dependencies have more than one version
widget_world: WARN release in the full dependency chain:
widget_world: WARN
widget_world: WARN * core/glibc ( core/glibc/2.29/ core/glibc/2.33/ )
widget_world: WARN
widget_world: WARN The current situation usually arises when a plan has a direct
widget_world: WARN dependency on one version of a package (acme/A/7.0/20160101200001)
widget_world: WARN and has a direct dependency on another package which itself depends
widget_world: WARN on another version of the same package (acme/A/2.0/20151201060001).
widget_world: WARN If this package (acme/A) contains shared libraries which are
widget_world: WARN loaded at runtime by the current plan, then both versions of
widget_world: WARN acme/A could be loaded into the same process in a potentially
widget_world: WARN surprising order. Worse, if both versions of acme/A are
widget_world: WARN ABI-incompatible, runtime segmentation faults are more than likely.
widget_world: WARN
widget_world: WARN In order to preserve reliability at runtime the duplicate dependency
widget_world: WARN entries will need to be resolved before this plan can be built.
widget_world: WARN Below is an expanded graph of all $pkg_deps and their dependencies
widget_world: WARN with the problematic lines noted.
widget_world: WARN
widget_world: WARN Computed dependency graph (Lines with '*' denote a problematic entry):

nellshamrell/widget_world/1.0.0/
nellshamrell/widget/1.0.0/ ()
core/glibc/2.33/ (
)
nellshamrell/world/1.0.0/ ()
core/glibc/2.29/ (
)
ERROR: Computed runtime dependency check failed, aborting

This error occurs because nellshamrell/widget_world has one dependency that was built with 2.33, and another that was built with 2.29. This makes them incompatible, and the nellshamrell/widget_world plan will not build.

How do I resolve this?

In this case, you can resolve this error by rebuilding nellshamrell/world with core/glibc version 2.33. If we do that, and now both dependencies depend on the same version of glibc, then nellshamrell/widget_world will build fine:

nellshamrell/widget_world
-> nellshamrell/widget
--> core/glibc/2.33
-> nellshamrell/world
--> core/glibc/2.33

Success!

2 Likes