Binary Wrapper Packages
While Chef Habitat provides the best behavior for applications that can be compiled from source into the Chef Habitat ecosystem, it can also bring the same management benefits to applications distributed in binary-only form.
You can write plans to package up these binary artifacts with minimal special handling. This article covers some tips and tricks for getting this software into Chef Habitat.
Override The Build Phases You Don’t Need
A Chef Habitat package build proceeds in phases: download, verification, unpacking (where you would also patch source code, if you had it), build, and finally installation. Each of these phases has default behavior within the build system.
When building binary packages, you override the behavior of phases that don’t apply to you. At the very minimum, you must override the do_build
and do_install
phases, for example:
(...)
do_build() {
# relocate library dependencies here, if needed---see next topic
return 0
}
do_install() {
mkdir -p $pkg_prefix/bin
cp $PLAN_CONTEXT/bin/hello_world $pkg_prefix/bin/hello_world
chmod +x $pkg_prefix/bin/hello_world
}
Relocate Hard-Coded Library Dependencies If Possible
On Linux, many binaries hardcode library dependencies to /lib
or /lib64
inside their ELF symbol table. Unfortunately, this means that Chef Habitat is unable to provide dependency isolation guarantees if packages are dependent on any operating system’s libraries in those directories. These Chef Habitat packages will also fail to run in minimal environments like containers built using hab-pkg-export-docker
, because there won’t be a glibc
inside /lib
or /lib64
.
Note
PATH
. You should make sure to include all dependencies in the pkg_deps
of a plan.ps1
to ensure all of their respective DLL
s are accessible by your application.Most binaries compiled in a full Linux environment have a hard dependency on /lib/ld-linux.so
or /lib/ld-linux-x86_64.so
. In order to relocate this dependency to the Chef Habitat-provided variant, which is provided by core/glibc
, use the patchelf(1)
utility within your plan:
Declare a build-time dependency on
core/patchelf
as part of yourpkg_build_deps
line.Invoke
patchelf
on any binaries with this problem during thedo_install()
phase. For example:patchelf --interpreter "$(pkg_path_for core/glibc)/lib/ld-linux-x86-64.so.2" \ "${pkg_prefix}/bin/somebinary"
The binary may have other hardcoded dependencies on its own libraries that you may need to relocate using other flags to
patchelf
like--rpath
. For example, Oracle Java provides additional libraries inlib/amd64/jli
that you will need to relocate to the Chef Habitat location:export LD_RUN_PATH=$LD_RUN_PATH:$pkg_prefix/lib/amd64/jli patchelf --interpreter "$(pkg_path_for core/glibc)/lib/ld-linux-x86-64.so.2" \ --set-rpath ${LD_RUN_PATH} "${pkg_prefix}/bin/java"
For more information, please see the patchelf documentation.
Relocating library dependencies
In some situations it will be impossible for you to relocate library dependencies using patchelf
as above. For example, if the version of glibc
the software requires is different than that provided by an available version of glibc
in a Chef Habitat package, attempting to patchelf
the program will cause execution to fail due to ABI incompatibility.
Your software vendor’s support policy might also prohibit you from modifying software that they ship you.
In these situations, you will have to give up Chef Habitat’s guarantees of complete dependency isolation and continue to rely on the library dependencies provided by the host operating system. However, you can continue to use the features of the Chef Habitat Supervisor that provide uniform manageability across your entire fleet of applications.
Fixing hardcoded interpreters
Binary packages often come with other utility scripts that have their interpreter or shebang hardcoded to a path that won’t exist under Chef Habitat. Examples are: #!/bin/sh
, #!/bin/bash
, #!/bin/env
or #!/usr/bin/perl
. You must modify these to point to the Chef Habitat-provided versions, and also declare a runtime dependency in your plan on the corresponding Chef Habitat package (for example, core/perl
).
Use the fix_interpreter
function within your plan to correct these interpreter lines during any phase, but most likely your do_build
phase. For example:
fix_interpreter ${target} core/coreutils bin/env
The arguments to fix_interpreter
are the file (represented here by ${target}
) you are trying to fix, the origin/name pair of the Chef Habitat package that provides that interpreter, and the interpreter pattern to search and replace in the target.
If you have many files you need to fix, or the binary package automatically generates scripts with hardcoded shebang lines, you may need to symlink Chef Habitat’s version into where the binary package expects it to go:
ln -sv $(pkg_path_for coreutils)/bin/env /usr/bin/env
This is a last resort as it breaks the dependency isolation guarantees of Chef Habitat.