Skip to content
This repository was archived by the owner on Jul 14, 2021. It is now read-only.

Improve packaging Chef-DK with Habitat #2202

Merged
merged 9 commits into from
Aug 5, 2019
Merged

Improve packaging Chef-DK with Habitat #2202

merged 9 commits into from
Aug 5, 2019

Conversation

afiune
Copy link
Contributor

@afiune afiune commented Jul 30, 2019

Improvements to the way we package Chef-DK with Habitat (for Linux systems)

Description

  • Restructure wrap_ruby_bin to respect DEBUG env-var
  • Restructure do_install to execute in a sub-shell
  • Restructure do_build to execute in a sub-shell
  • Use SRC_PATH instead of PLAN_CONTEXT/..
  • Remove unnecessary do_before callback
  • Use pkg_interpreter_for and remove core/coreutils dependency
  • Makes pkg_version a func to use update_pkg_version
  • Adds buildtike job to test habitat package to prevent regressions
  • Quick general styling

Related Issue

None

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Chore (non-breaking change that does not add functionality or fix an issue)

Checklist:

  • I have read the CONTRIBUTING document.
  • I have run the pre-merge tests locally and they pass.
  • I have updated the documentation accordingly.
  • I have added tests to cover my changes.
  • All new and existing tests passed.
  • All commits have been signed-off for the Developer Certificate of Origin.

@afiune afiune requested review from a team as code owners July 30, 2019 17:13
@afiune afiune self-assigned this Jul 30, 2019
@afiune afiune added the Aspect: Packaging Distribution of the projects 'compiled' artifacts. label Jul 30, 2019
@robbkidd
Copy link
Contributor

@afiune So that I can learn more about these patterns for plans, can you include the Why for each of these changes (in the list of changes on the PR? maybe in the git commit messages themselves?) in addition to the What of the change?

@afiune
Copy link
Contributor Author

afiune commented Jul 30, 2019

@robbkidd I tried to do it by separating the change in different commits but, I can be more explicit with in-line comments. Gimme a few! 💯

@@ -33,17 +34,9 @@ pkg_deps=(
core/libarchive
)

pkg_svc_user=root
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better to set variables at the top to have better visibility.

habitat/plan.sh Outdated
do_before() {
do_default_before
update_pkg_version
}
Copy link
Contributor Author

@afiune afiune Jul 30, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole do_before wasn't doing anything, I feel it was a copy/paste from other plans, maybe. So better to have less code to not confuse users that we are using this hook when we are not.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be using the do_before to load the package version rather than cat-ing it in the pkg_version declaration.

# versions of dependencies already resolved
build_line "AppBundling chef-dk gems: ${gems_to_appbundle[*]}"
( cd "$CACHE_PATH" || exit_with "unable to enter hab-cache directory" 1
bundle exec appbundler "$HAB_CACHE_SRC_PATH/$pkg_dirname" "$ruby_bin_dir" ${gems_to_appbundle[*]} >/dev/null
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

w( °o°)w ... appbundler will accept multiple gems on the command line?

do_download() {
# Instead of downloading, build a gem based on the source in src/
cd $PLAN_CONTEXT/..
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a best practice, we /shouldn't/ change the directory inside the callbacks since that position will be persistent throughout the build process of the package and latter other callbacks or habitat internals might conflict with that change of state, instead we should 1) execute commands that depend on a specific path inside a sub-sells or 2) use pushd and popd to move to a directory and get back where we were before.

Number 1) is more desired but you could use them both.

Here we actually don't even need to change the directory, so we should try to avoid it.

@@ -53,7 +46,8 @@ do_verify() {
do_unpack() {
# Unpack the gem we built to the source cache path. Building then unpacking
# the gem reuses the file inclusion/exclusion rules defined in the gemspec.
gem unpack $PLAN_CONTEXT/../$pkg_name-$pkg_version.gem --target=$HAB_CACHE_SRC_PATH
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We were using $PLAN_CONTEXT/.. to go to /src, habitat has an environment to go there directly, let us use it! SRC_PATH 💯

}

do_build() {
cd $CACHE_PATH
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, avoid changing directories like this. Use sub-shells instead. See line 87

@@ -64,40 +58,46 @@ do_prepare() {
build_line "Ruby ABI version appears to be ${RUBY_ABI_VERSION}"

build_line "Setting link for /usr/bin/env to 'coreutils'"
[[ ! -f /usr/bin/env ]] && ln -s $(pkg_path_for coreutils)/bin/env /usr/bin/env

return 0
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we always return 0 at the end of this function, we would be saying that this function always succeeds.

local _zlib_dir
export NOKOGIRI_CONFIG
export GEM_HOME
export GEM_PATH
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is safer to first initialize the variables and then set the values, especially if you don't know what the content of the variable could be.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a pattern that has come from when the value is from a command interpolation, right? Because the exit code gets eaten and testing success would be on local? So, just get in the habit of initializing locals first, then assigning values?

bundle config --local silence_root_warning 1
bundle install --without dep_selector --no-deployment --jobs 2 --retry 5 --path "$pkg_prefix"
gem build ${pkg_name}.gemspec
)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All these bundle commands depend to run inside the cache path, here is the way you create sub-shells to run those commands.

}

do_install() {
cd $CACHE_PATH
Copy link
Contributor Author

@afiune afiune Jul 30, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, try to avoid changing paths 😅

# versions of dependencies already resolved
build_line "AppBundling chef-dk gems: ${gems_to_appbundle[*]}"
( cd "$CACHE_PATH" || exit_with "unable to enter hab-cache directory" 1
bundle exec appbundler "$HAB_CACHE_SRC_PATH/$pkg_dirname" "$ruby_bin_dir" ${gems_to_appbundle[*]} >/dev/null
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when both paths are the same, appbundler allows you to pass a list of gems to bundle, let us use that instead of doing a for here since we need to execute it inside a sub-shell.

for exe in $pkg_prefix/ruby-bin/*; do
wrap_ruby_bin $(basename ${exe})
build_line "Installing generated gem. (${CACHE_PATH}/${pkg_name}-${pkg_version}.gem)"
gem install --no-doc "${CACHE_PATH}/${pkg_name}-${pkg_version}.gem"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this gem install command was inside the do_build callback, were as now it is inside the do_install since we are installing it 😉


build_line "Link the appbundled binstubs into the package's bin directory"
for exe in "$ruby_bin_dir"/*; do
wrap_ruby_bin "$(basename "${exe}")"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wrap_ruby_bin helper method doesn't need to run on a specific path so, we avoid using unnecessary sub-shells.


# Appbundling gems speeds up runtime by creating binstubs for Ruby executables with
# versions of dependencies already resolved
# TODO(afiune) Should we define this inside the repo and not here inside the plan?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree that this list should be maintained in one place for all the buildy things to reference. Question is: where to store the list and in what format?

cat <<EOF > "$wrapper"
#!$(pkg_path_for busybox-static)/bin/sh
set -e
if test -n "$DEBUG"; then set -x; fi
if test -n "\$DEBUG"; then set -x; fi
Copy link
Contributor Author

@afiune afiune Jul 30, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before we were actually printing the content of the variable, now we are writing the variable name to check it at execution of these binaries, you can now do this:

[86][default:/src:1]# DEBUG=true chef
+ export 'GEM_HOME=/hab/pkgs/afiune/chef-dk/4.3.3/20190730094053/ruby/2.6.0/'
+ export 'GEM_PATH=/hab/pkgs/core/ruby26/2.6.3/20190515141800/lib/ruby/gems/2.6.0:/hab/pkgs/core/bundler/1.17.3/20190416230243:/hab/pkgs/afiune/chef-dk/4.3.3/20190730094053/ruby/2.6.0/:/hab/pkgs/afiune/chef-dk/4.3.3/20190730094053'
+ export 'SSL_CERT_FILE=/hab/pkgs/core/cacerts/2018.12.05/20190115014206/ssl/cert.pem'
+ export 'APPBUNDLER_ALLOW_RVM=true'
+ unset RUBYOPT GEMRC
+ exec /hab/pkgs/core/ruby26/2.6.3/20190515141800/bin/ruby /hab/pkgs/afiune/chef-dk/4.3.3/20190730094053/ruby-bin/chef
Usage:
    chef -h/--help
    chef -v/--version
    chef command [arguments...] [options...]


Available Commands:
    exec                    Runs the command in context of the embedded ruby
    env                     Prints environment variables used by ChefDK
    gem                     Runs the `gem` command in context of the embedded Ruby
    generate                Generate a new repository, cookbook, or other component
    shell-init              Initialize your shell to use ChefDK as your primary Ruby
    install                 Install cookbooks from a Policyfile and generate a locked cookbook set
    update                  Updates a Policyfile.lock.json with latest run_list and cookbooks
    push                    Push a local policy lock to a policy group on the Chef Infra Server
    push-archive            Push a policy archive to a policy group on the Chef Infra Server
    show-policy             Show policyfile objects on the Chef Infra Server
    diff                    Generate an itemized diff of two Policyfile lock documents
    export                  Export a policy lock as a Chef Infra Zero code repo
    clean-policy-revisions  Delete unused policy revisions on the Chef Infra Server
    clean-policy-cookbooks  Delete unused policyfile cookbooks on the Chef Infra Server
    delete-policy-group     Delete a policy group on the Chef Infra Server
    delete-policy           Delete all revisions of a policy on the Chef Infra Server
    undelete                Undo a delete command
    describe-cookbook       Prints cookbook checksum information used for cookbook identifier

export GEM_HOME="$pkg_prefix/ruby/${RUBY_ABI_VERSION}/"
export GEM_PATH="$(pkg_path_for ${ruby_pkg})/lib/ruby/gems/${RUBY_ABI_VERSION}:$(hab pkg path core/bundler):$pkg_prefix/ruby/${RUBY_ABI_VERSION}/:$GEM_HOME"
export SSL_CERT_FILE=$(pkg_path_for core/cacerts)/ssl/cert.pem
export APPBUNDLER_ALLOW_RVM=true
unset RUBYOPT GEMRC
exec $(pkg_path_for ${ruby_pkg})/bin/ruby ${real_cmd} \$@
exec $(pkg_path_for ${ruby_pkg})/bin/ruby ${real_cmd} "\$@"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a best practice, we should double-quote array expansions to avoid re-splitting elements.

@afiune afiune requested a review from a team as a code owner August 1, 2019 12:34
@afiune afiune force-pushed the afiune/hab branch 3 times, most recently from f38f1da to 1ee692e Compare August 1, 2019 13:56
habitat/plan.sh Outdated
do_before() {
do_default_before
update_pkg_version
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be using the do_before to load the package version rather than cat-ing it in the pkg_version declaration.

@afiune
Copy link
Contributor Author

afiune commented Aug 1, 2019

@tduffield I can put back update_pkg_version but then I will make the pkg_version a function, would that be ok?

out-of-curiosity: what is the motivation to use this pattern Vs cat-ing?

@tduffield
Copy link
Contributor

tduffield commented Aug 1, 2019

@afiune it's what the Habitat docs recommend: https://www.habitat.sh/docs/reference/#plan-helper-functions. I don't recall the exact reasoning, but I think it has to do with whether or not the path is correct based on where we source the plan file.

@afiune
Copy link
Contributor Author

afiune commented Aug 1, 2019

@tduffield makes sense, I have put it back and converted the pkg_version into a function. Thanks for your feedback!

Salim Afiune added 9 commits August 1, 2019 20:16
The whole do_before wasn't doing anything, I feel it was a copy/paste from
other plans, maybe. So better to have less code to not confuse users that
we are using this hook when we are not.

Signed-off-by: Salim Afiune <afiune@chef.io>
We were using `$PLAN_CONTEXT/..` to go to `/src` but habitat has an
environment to go there directly, let us use it! `SRC_PATH`

Signed-off-by: Salim Afiune <afiune@chef.io>
It's better to set variables at the top to have better visibility.

If we always return 0 at the end of this function, we would be
saying that this function always succeeds.

Signed-off-by: Salim Afiune <afiune@chef.io>
As a best practice, we /shouldn't/ change the directory inside the
callbacks since that position will be persistent throughout the build
process of the package and latter other callbacks or habitat internals
might conflict with that change of state, instead we should 1) execute
commands that depend on a specific path inside a sub-sells or 2) use
pushd and popd to move to a directory and get back where we were before.

Number 1) is more desired but you could use them both.

It is safer to first initialize the variables and then set the values,
especially if you don't know what the content of the variable could be.

All the bundle commands depend on running inside the cache path, I
changed it to use sub-shells to run those commands.

Signed-off-by: Salim Afiune <afiune@chef.io>
As a best practice, we /shouldn't/ change the directory inside the
callbacks since that position will be persistent throughout the build
process of the package and latter other callbacks or habitat internals
might conflict with that change of state, instead we should 1) execute
commands that depend on a specific path inside a sub-sells or 2) use
pushd and popd to move to a directory and get back where we were before.

Number 1) is more desired but you could use them both.

TODO we should define the list of binaries inside the repo and not inside
the plan

Signed-off-by: Salim Afiune <afiune@chef.io>
As a best practice, we should double-quote array expansions like `"\$@"`
to avoid re-splitting elements.

Before we were actually printing the content of the variable, now we
are writing the variable name to check it at execution of these binaries,
you can now do this:
```
[86][default:/src:1]# DEBUG=true chef
+ export 'GEM_HOME=/hab/pkgs/afiune/chef-dk/4.3.3/20190730094053/ruby/2.6.0/'
+ export 'GEM_PATH=/hab/pkgs/core/ruby26/2.6.3/20190515141800/lib/ruby/gems/2.6.0:/hab/pkgs/core/bundler/1.17.3/20190416230243:/hab/pkgs/afiune/chef-dk/4.3.3/20190730094053/ruby/2.6.0/:/hab/pkgs/afiune/chef-dk/4.3.3/20190730094053'
+ export 'SSL_CERT_FILE=/hab/pkgs/core/cacerts/2018.12.05/20190115014206/ssl/cert.pem'
+ export 'APPBUNDLER_ALLOW_RVM=true'
+ unset RUBYOPT GEMRC
+ exec /hab/pkgs/core/ruby26/2.6.3/20190515141800/bin/ruby /hab/pkgs/afiune/chef-dk/4.3.3/20190730094053/ruby-bin/chef
Usage:
    chef -h/--help
    chef -v/--version
    chef command [arguments...] [options...]

Available Commands:
    exec                    Runs the command in context of the embedded ruby
    env                     Prints environment variables used by ChefDK
    gem                     Runs the `gem` command in context of the embedded Ruby
    generate                Generate a new repository, cookbook, or other component
    shell-init              Initialize your shell to use ChefDK as your primary Ruby
    install                 Install cookbooks from a Policyfile and generate a locked cookbook set
    update                  Updates a Policyfile.lock.json with latest run_list and cookbooks
    push                    Push a local policy lock to a policy group on the Chef Infra Server
    push-archive            Push a policy archive to a policy group on the Chef Infra Server
    show-policy             Show policyfile objects on the Chef Infra Server
    diff                    Generate an itemized diff of two Policyfile lock documents
    export                  Export a policy lock as a Chef Infra Zero code repo
    clean-policy-revisions  Delete unused policy revisions on the Chef Infra Server
    clean-policy-cookbooks  Delete unused policyfile cookbooks on the Chef Infra Server
    delete-policy-group     Delete a policy group on the Chef Infra Server
    delete-policy           Delete all revisions of a policy on the Chef Infra Server
    undelete                Undo a delete command
    describe-cookbook       Prints cookbook checksum information used for cookbook identifier
```

Signed-off-by: Salim Afiune <afiune@chef.io>
Why? Because if we have code inside the repo that is not being tested we
will definitely loose this work while doing other modifications, the
best thing to do is always add tests to constantly check if any
functionality is lost.

TODO Improve this tests a lot since right now they are just printing the
version of the commands that work, but at least we will know if break
one for the habitat packages.

Signed-off-by: Salim Afiune <afiune@chef.io>
As a best practice we should use the `update_pkg_version` as habitat
documents it here: https://www.habitat.sh/docs/reference/#plan-helper-functions

Signed-off-by: Salim Afiune <afiune@chef.io>
We should use proper habitat helper methods to get this information like
a package interpreter: https://www.habitat.sh/docs/reference/#plan-helper-functions

Signed-off-by: Salim Afiune <afiune@chef.io>
@afiune
Copy link
Contributor Author

afiune commented Aug 1, 2019

Apologies for the force-pushed but there was a request to have all the in-line comments inside every commit message so I had to rebase them all to do so. Now, this PR should be ready to review and merge!

tenor-252065473

@afiune afiune added the Type: Chore non-critical maintenance of a project. label Aug 2, 2019
cd $PLAN_CONTEXT/..
gem build $pkg_name.gemspec
build_line "Building gem from source. (${SRC_PATH}/${pkg_name}.gemspec)"
gem build "${SRC_PATH}/${pkg_name}.gemspec"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To confirm, ${SRC_PATH} is the directory the studio was entered from, right? Which should be fine for standard builds of this code base. Noting that this could surprise someone in their development environment if they enter a studio from higher up in a directory structure if they are trying to work with multiple plans in a single studio.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can't enter from higher up, you have to enter from the source root path so that the source is available inside the studio (docker container)

echo -n " - chef-shell "
chef-shell -v | awk {'print $NF'}
# TODO(afiune) do we still use this? because it doesn't work
echo " - chef-zero (not-working)"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we do still need this. Maybe chef-zero got munched in the shuffling of what gem provides executables post-license change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is there, it just doesn't work. I'll fix it on further PRs

Copy link
Contributor

@robbkidd robbkidd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I asked more questions, but I don't think they block merging.

Thank you for your commit message commentary!
thank you

@afiune afiune merged commit cc332fe into master Aug 5, 2019
@chef-ci chef-ci deleted the afiune/hab branch August 5, 2019 09:24
@lock
Copy link

lock bot commented Oct 4, 2019

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked as resolved and limited conversation to collaborators Oct 4, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Aspect: Packaging Distribution of the projects 'compiled' artifacts. Type: Chore non-critical maintenance of a project.
Development

Successfully merging this pull request may close these issues.

3 participants