Using Scaffolding envvars in init hook

I use the ruby scaffolding to manage one of my packages, and I have a bunch of scaffolding envvars set to control the config of the running service. So, for example, I have scaffolding_env[FOO]="bar" set.

I’d like to access that var in an init hook to control the behavior of part of the init process, but I haven’t found a way to do that. I’ve tried just accessing the set envvar using $FOO, but it seems unset at the time init runs. I’ve tried explicitly sourcing the app_env.sh as well, but that doesn’t seem to be rendered during init either. Finally, I tried a mustache helper in the init (which the docs indicate should work for “normal” envvars) like {{pkg.env.FOO}}, but that doesn’t work either. They all just render empty, and/or throw an error in the case of sourcing the app_env.sh.

Has anyone done this successfully? Do I need to namespace my mustache-helper differently? Is this even possible?

Thanks!

Relevant snippet of my init hook with my attempt at using the mustache helper:

#!/bin/sh
set -e

# Set envvars based on rendered config.
# This might be redundant, but doing it again doesn't hurt anything
#source {{$svc_config_path}}/app_env.sh

# initialize DB. Normal logic applies here, non-zero means failure.
pg_isready -d {{pkg.env.DATABASE_URL}}
if [ $? -eq 1 ]
then
  app-rake db:create
  app-rake db:seed
else
  echo "Database is ready! Skipping create..."
fi

Snippet of my plan where the scaffolding vars are set:

pkg_name=app
pkg_origin=my_org
pkg_version="0.1.0"
pkg_scaffolding="core/scaffolding-ruby"
pkg_deps=(core/curl)

declare -A scaffolding_env
scaffolding_env[DATABASE_URL]="postgres://db_user:db_pw@db_server:5432/db"

Do you see DATABASE_URL get written out to app_env.sh correctly? There should be something like

export DATABASE_URL='postgres://db_user:db_pw@db_server:5432/db'

in there. Also, what steps are you executing to build and run?

I see it in app_env.sh once it’s rendered, but that doesn’t seem to have happened at the time that the init hook runs. It is used correctly by the application itself.

Build and run steps are both default provided by the scaffolding.

In order to reproduce the behavior you’re seeing, please show exactly the commands that you’re executing and what output you’re getting. To the extent possible, try including minimal versions of your plan.sh, init hook and any other relevant plan context files.

It may also help to add DEBUG=1 to the command that should trigger the init hook so we can see if app_env.sh is being sourced as it should be.

I don’t have any command that I’ve explicitly written that triggers the init hook. I just created it and let habitat (maybe the ruby scaffolding?) consume and execute it as it normally would.

How would I specify that and add the DEBUG=1 option to it?

After you make changes to your plan.sh or init hook, what commands are you running to reproduce the behavior you’re seeing? Are you building locally in the studio, or pushing to builder and installing from there? What does the build output for your package look like? What command are you using to run your built package?

As for DEBUG=1, if you were running your package like this:

hab svc load /path/to/foo.hart

then you can

env DEBUG=1 hab svc load /path/to/foo.hart

or (assuming bash)

export DEBUG=1

and then all commands you run will generate extra debugging.

I’m building the package:

hab studio enter
build

Then packaging for docker:

hab pkg docker export results/latest_thing_I_built.hart

Then running the container w/ docker:

docker run local/my_app:latest

I’ll see about getting the additional information collected.

Perhaps a more fundamental question though, should I expect the app_env.sh file to be rendered at init time?

Please include the output from your various commands; there may be some hints about any errors that could be occurring.

Also, what does app_env.sh contain? Does it change when you add or remove something like

scaffolding_env[FOO]=bar

to your plan.sh and then re-run the build command? What about when loading the package?

Also, what if you run your app directly in the studio from the built package to rule out the issue being with the docker export? You should be able to run

hab svc load results/latest_thing_I_built.hart

Does that come up correctly? What sort of output do you see when you run

hab sup status

And if your package shows itself as up, what output do you see in the supervisor log at /hab/sup/default/sup.log?

Based on the documentation on defining environment variables and adding configuration, I would expect something like

scaffolding_env[FOO]=bar

in your plan.sh to be accessible in the context of the init hook. From the second link:

The following environment variables are set by the Ruby scaffolding and placed in a file called app_env.sh, which is created by the scaffolding and located in the /hab/svc/package/config/ directory. When the init hook is executed, the following export commands are run in the context of the hook, which allows it to have access to connection string data and deployment values.

I think the issue is

  1. If you’re overriding the default init hook provided by the scaffolding, which means you need to source app_env.sh in your own hook. If you don’t need special init hook behavior, you can get rid of your habitat/hooks/init file and when you rebuild, you should get a scaffolding-generated hook that does the right thing. You can check /hab/pkgs/jbauman/you-app-name/version/buildnumber/hooks/init to confirm what’s getting generated by the build.

  2. Your code had source {{$svc_config_path}}/app_env.sh, but that’s not quite right. It needs to be {{pkg.svc_config_path}}, so the full line would look like:

    source “{{pkg.svc_config_path}}/app_env.sh”

I’ll show a full example shortly.

I just noticed that same error in the init as well after grepping through some core packages for reference. Building an update now to see if it fixes things…

Starting from https://github.com/habitat-sh/sample-node-app, here’s what I did to test.

In habitat/plan.sh:

declare -A scaffolding_env
scaffolding_env[FOO]="bar"

In habitat/hooks/init:

#!/bin/bash
source "{{pkg.svc_config_path}}/app_env.sh"
echo "I am the init hook, FOO='$FOO'"

Then

[56][default:/src:0]# build
…
[58][default:/src:1]# hab svc load /src/results/jbauman-sample-node-app-1.0.1-20180417204016-x86_64-linux.hart
hab-sup(MN): The jbauman/sample-node-app/1.0.1/20180417204016 service was successfully loaded
[59][default:/src:0]# sl
…
sample-node-app.default hook[init]:(HK): I am the init hook, FOO='bar'

Testing locally that seems to have fixed the problem. I’m able to access the envvars set by app_env.sh within my init now and I see the {{pkg.svc_config_path}} rendered out as it should be.

Now that I know how this works I’m going to see if I can put together a PR for the docs to make this more clear.

Thanks for talking through things!

2 Likes

I don’t see the docs anywhere in github so I could do a PR on them. Am I missing something?

In any case, it would be useful if the section https://www.habitat.sh/docs/reference/#template-data made it more explicit how this stuff is namespaced. I was guessing what it should be based on what I saw in another section and clearly guessed wrong.

Could you help me understand what your expectation was based on what you saw elsewhere, and how that differed from what @baumanj directed you to?

If there’s anything misleading or unclear, we definitely want to fix that, but if you could be explicit about the nature of your misunderstanding, and how you were led astray by the docs, that would be super helpful.

In the Template data section, you have the top level namespaces broken out, with their members below them, but it never says “treat these as name spaces and when using them in mustache tags use them like {{pkg.the_thing}}, or {{sys.the_other_thing}}” or similar. Just one or two sentences like that in the intro where readers are pointed to the example would have made all the difference for me.

This is implied in the example, but since the example only dealt with the cfg space it wasn’t clear from that it was how things are broken up, or that it was actually a namespace. It seems like a special case because of how it’s treated in the docs. In the end, it’s one of those things that makes perfect sense once you understand it, but if you don’t know about it already, making it more explicit in the documentation would be very helpful in putting the pieces together.

1 Like

That is wonderful feedback @qhartman thank you for taking the time to spell that out!

That’s super helpful - thank you. I agree, some explicit discussion of namespacing would make a big difference.