Is it possible to do if/else conditional logic in Handlebars templates?


#1

I want our app servers to run a scheduler process, Celery Beat, if they are the 1st group member, and if not, to run Celery Worker. Is this possible with Habitat-flavored templates?


#2

Have you considered doing this with a leader/follower setup?

Then you allow the supervisor ring to do election, and you can use:

{{#if svc.me.follower ~}}
  ... do stuff ...
{/if ~}}

Reference: https://www.habitat.sh/docs/reference/


#3

Good idea. I think that would work, and would have the added advantage of ensuring that the scheduler restarts before the workers when the service is updated.


#4

The downside to this approach is that, for local development, one needs a quorum of peers to unblock the topology leader. Since the Studio can’t run more than one instance of a service I’d need to jury-rig something with Docker Compose or similar, which means a much clunkier workflow.


#5

I’m also getting some strange template rendering errors:

Failed to load hook: hab-sup(ER)[components/sup/src/error.rs:473:9]: TemplateError(TemplateError { reason: InvalidSyntax, template_name: Some("run"), line_no: Some(31), column_no: Some(1) })

Template contents:

#!{{pkgPathFor "core/bash"}}/bin/bash -ex

export HOME="/hab/svc/{{pkg.name}}/data"
export FLASK_APP="/hab/svc/config/hello.py"
export FLASK_APP="{{pkg.path}}/hello.py"
export FLASK_ENV="development"
cd /hab/svc/{{pkg.name}}

if [ "{{cfg.role}}" == "web_server" ]
then
  exec flask run --host="{{cfg.host}}" 2>&1
elif [ "{{cfg.role}}" == "background_tasks" ]
then
{{#if svc.me.leader ~}}
  exec celery beat \
    --app="tasks" \
    --broker="{{cfg.celery_broker_url}}" \
    --loglevel="info" \
    --schedule="{{pkg.svc_data_path}}/{{pkg.name}}-schedule" 2>&1
{/if ~}}
{{#if svc.me.follower ~}}
  exec celery worker \
    --app="tasks" \
    --broker="{{cfg.celery_broker_url}}" \
    --loglevel="info" 2>&1
{/if ~}}
else
  echo "{{cfg.role}} is not supported. 'role' must be either 'web_server' or 'background_tasks'."
  exit 1
fi

My Handlebars syntax looks good to me - am I missing something?


#6

Ahhhh - missing 2 left curly braces :smiley:


#7

So the problem remains for the local Studio dev environment:
Waiting to execute hooks; election in progress, and we have no quorum.


#8

Do you need both the leader and the follower running for local dev?


#9

Actually yes, we would. We must always have both the Celery Beat scheduler and at least one Celery Worker to exercise async/background tasks. In the absence of a Worker I’d accept, for now, just running the Beat scheduler so that we can verify that jobs are enqueued successfully.


#10

I think you’re option here is going to be limited to a docker-compose setup due to the limitations of running the same service under a single supervisor. I was going to suggest checking if you were in leader-follower in the template but our version of handlebars doesn’t support string equality testing yet.


#11

Is there a way of rendering out at least a string to tell us what topology we are using? I could then use a shell conditional in my run hook to change my callable depending on the topology style.


#12

I think you can use svc.me.topology which will render standalone or leader


#13

That appears to render an empty string. I’m using the 0.59.0 release.


#14

you might not need the .me


#15

I know it’s available because I can see it in the output for curl localhost:9631/services


#16

I see that when cURLing the Supervisor API also, but I still don’t get a rendered value for {{svc.topology}} in my template…


#17

Now that I look further, we may not expose that through templates


#18

Okay - I had thought that we could access any object nested inside sys, svc, pkg, etc. - if that’s not necessarily true, is there a good way to see what’s available to the templating engine? Maybe the right answer is “read the docs”, but I seem to recall the docs sometimes have been a bit out of date.


#19

@bixu The docs here are actually quite up-to-date, as they are automatically generated from code.

https://www.habitat.sh/docs/reference/#template-data

We’ve got the templating data defined with JSON Schema: https://github.com/habitat-sh/habitat/blob/388f1df1ac54e75138a15a01f1c6023306630701/components/sup/doc/render_context_schema.json


#20

@cnunciato made a pass through the docs and cleaned them up iirc. They’re under template variables