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

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?

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

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

{{#if ~}}
  ... do stuff ...
{/if ~}}


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.

1 Like

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.

I’m also getting some strange template rendering errors:

Failed to load hook: hab-sup(ER)[components/sup/src/]: 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/{{}}/data"
export FLASK_APP="/hab/svc/config/"
export FLASK_APP="{{pkg.path}}/"
export FLASK_ENV="development"
cd /hab/svc/{{}}

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

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

Ahhhh - missing 2 left curly braces :smiley:

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

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

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.

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.

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.

I think you can use which will render standalone or leader

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

you might not need the .me

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

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

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

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.

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

We’ve got the templating data defined with JSON Schema:

1 Like

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

1 Like