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 do not 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 will not be a glibc
inside /lib
or /lib64
.
Note: On Windows, library dependency locations are not maintained in a binary file’s headers. See this MSDN article for a complete explanation of how Windows binaries are located. However, it’s typically sufficient to ensure that the dependent binaries are on the
PATH
. You should make sure to include all dependencies in thepkg_deps
of aplan.ps1
to ensure all of their respectiveDLL
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”, line (first line of a script) hardcoded to a path that will not exist under Chef Habitat. Examples are: #!/bin/sh
, #!/bin/bash
, #!/bin/env
or #!/usr/bin/perl
. It is necessary to 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 simply 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.