Producer/consumer: export path of producer package

Our frontend build produces a single artifact which contains the files to be served for our single page app (spa) as well as files required for server side rendering (ssr). We already have set up habitat a while ago to server the spa files via Nginx.

Now I would like to add a second habitat package, which contains the server needed to serve the server side rendered requests. This ssr package requires the files contained in the spa package which is served by Nginx. So, I tried to set up a producer consumer contract, where the producer (spa) exports the path to its compiled files. The ssr package would then pick up the path to the spa package and start its own server, which handles server side rendering.

I tried to set up the producer to export the path within the plan

pkg_exports=(
  [distPath]=distPath
)

and added a default value to the toml file:
distPath = "${pkg_prefix}/dist"

Unfortunately I only see a blank value within the rendered configuration file of the ssr package (/hab/svc/ssr/config/server.js). The handlebars code used in the config file as well as the run hook looks like this:
{{bind.nginx.first.distPath}}

Two questions here:

  • Is there a better way to export or set the path than from the toml file (which does not seem to work - at least I seem to be doing something the wrong way)?
  • Is there a way to query exports and their values via hab command line. I noticed there is hab pkg binds, however this only seems to list the required bindings, not the exported values

Side note: the server side rendering requires the spa files to work, that’s why they are bundled together. A page loaded via ssr will load all the additional files required for the spa in the background. So, ssr merely is a way to decrease loading times. Alternatively it would work if one habitat package would launch Nginx as well the ssr server, however I figured that would not be a very good thing to do as we are not supposed to have two run hooks afaik…

After some trial and error I found that the binding lives under the cfg object within the member. The correct way of addressing the binding seems to be: {{bind.nginx.first.cfg.distPath}}

Unfortunately, this only provides the raw string value: {{pkg.path}}/dist which is useless in this case.

Is there any way to export the current path of the package and share it with another package?

I am going to try to add a dependency to the spa package within the ssr plan and see if I can make any progress that way. However, I am doubtful that I will find a good way to reload the ssr service, when the spa package (and such the distributed files) is updated.

unfortunately (at least in this case), when defining a dependency to the package, this will be more of a compile time dependency than a runtime dependency. So this will not do either.

Is there any way to have a service in habitat, which makes use of the files of another service (package)?

The only two requirements basically are that the service needs to reload in case when the files of the other service are updated (new version installed) and that it is able to access the files (within the same physical node). So, this actually seems to be something very simple, however I am struggling to find a way to model this with habitat.

Or maybe… could there possibly be a way to reliably load two services within one package? So that a frontend package would both start the SSR server as well as Nginx? Something that would correspond to having two run hooks?

Despite being able to think about a lot of really ugly workarounds (which all in some way undermine habitats core concepts) to solve this, the only reasonable solution that seems to come to my mind is to double up the deployments.
By using subfolders we probably should be able to build two independent habitat packages from within the same codebase. Then we will deploy all files redundantly - once bundled with the Nginx server - and once together with the SSR server. Each update to the application would then need to trigger both deployments. Still not a very good solution, but I don’t seem to be able to build this around habitats limitations in a reasonable way. I have reread most of the docs, but still can not find anything that would help in building something similar.

We had a discussion in the slack channel the other day and I’ll try to summarise that briefly.

Things that depend on a shared file system (as in one package being able to access the content of another without specifying a compile time dependency) currently are not supported. However, as this issue came up in the past already, thanks to @eeyun we now have this GitHub issue.

It has been recommended that I’ll use three plans to deal with my issue:

  • a plan which contains the static files (frontend build)
  • a plan, that depends on the static package, which contains service 1
  • a plan, that depends on the static package, which contains service 2

This requires two deployments for each new release of the application, as there are two services to be deployed. However, it makes working with bindings a lot easier for now.

1 Like