Application ERB location for form.yml.erb and submit.yml.erb

I’m working on shared templates between multiple interactive apps but am running into issues debugging some templating ERB errors with form.yml.erb and submit.yml.erb. It would help if I could view the resulting .yml files but I cannot find where these are stored.

Here is an example of what I’m doing:

<%-
require 'yaml'
require 'open3'
begin
  template_root = File.expand_path(File.join(__dir__, '..', 'templates'))
  template_submit = YAML.load( ERB.new(File.read(File.join(template_root, 'submit.yml.erb')), trim_mode: '-').result(binding) )
rescue => e
  puts e.message.strip
end
-%>
---
batch_connect:
  template: basic
  conn_params:
    - csrftoken

script:
  queue_name: <%= template_submit['script']['queue_name'] -%>
  native:
<%= YAML.dump(template_submit['script']['native']).gsub("---\n", '').gsub(/^/,"    ") -%>

Which loads data from a shared template that looks like:

---
script:
  queue_name: <%= custom_queue %>
  native:
    - "--nodes"
    - "<%= bc_num_slots.blank? ? 1 : bc_num_slots.to_i %>"
    - "--ntasks"
    - "<%= bc_num_slots.blank? ? 1 : bc_num_slots.to_i %>"
    - "--cpus-per-task"
    - "<%= num_cpus.blank? ? 1 : num_cpus.to_i %>"
    - "--mem"
    - "<%= num_mem.blank? ? 1 : num_mem.to_i %>G"

    <%- unless email.blank? -%>
    - "--mail-user"
    - "<%= email %>"
    - "--mail-type"
    - "BEGIN,END,FAIL"
    <%- end -%>

    <%- if num_gpus.to_i >0 -%>
    - "--gpus-per-task"
    - "<%= num_gpus.to_i %>" 
    <%- end -%>

    <%- if custom_queue.to_s == "backfill" -%>
    - "--requeue"
    <%- end -%>

    <%- unless node_list.blank? -%>
    - "--nodelist"
    - "<%= node_list %>"
    <%- end -%>

    <%- unless node_features_constraint.blank? -%>
    - "--constraint"
    - "<%= node_features_constraint %>"
    <%- end -%>

    <%- unless node_features_prefer.blank? -%>
    - "--prefer"
    - "<%= node_features_prefer %>"
    <%- end -%>

I’ve validated that the queue and slurm flags are being correctly set on job submission. However, the ‘csrftoken’ from the ‘con_params’ is being mangled somehow as denoted by rserver error logs. The only change I’ve made to the current app was with the submit.yml.erb which somehow introduced these errors.

2023-12-08T18:37:55.650710Z [rserver] INFO Reading rserver configuration from '/etc/rstudio/rserver.conf'
2023-12-08T18:37:55.654471Z [rserver] INFO Running as server user 'nvonwolf' (without privilege)
2023-12-08T18:37:55.656314Z [rserver] INFO Running without privilege; generating secure key at /tmp/rstudio-server/secure-cookie-key
2023-12-08T18:37:55.656623Z [rserver] INFO Reading database configuration from '/etc/rstudio/database.conf'
2023-12-08T18:37:55.656766Z [rserver] INFO Connecting to sqlite3 database at /var/lib/rstudio-server/rstudio-os.sqlite
2023-12-08T18:37:55.656859Z [rserver] INFO Creating database connection pool of size 20 (source: default maximum with 20 CPUs)
2023-12-08T18:37:55.666737Z [rserver] INFO Database schema has not been created yet. Creating database schema...
2023-12-08T18:37:55.669070Z [rserver] INFO No environment variables found at /etc/rstudio/env-vars
2023-12-08T18:38:11.410641Z [rserver] ERROR Failed to validate sign-in with invalid CSRF form; LOGGED FROM: bool rstudio::server::auth::common::validateSignIn(const rstudio::core::http::Request&, rstudio::core::http::Response*) src/cpp/server/auth/ServerAuthCommon.cpp:132
2023-12-08T18:38:19.421110Z [rserver] ERROR Failed to validate sign-in with invalid CSRF form; LOGGED FROM: bool rstudio::server::auth::common::validateSignIn(const rstudio::core::http::Request&, rstudio::core::http::Response*) src/cpp/server/auth/ServerAuthCommon.cpp:132
2023-12-08T18:39:04.724633Z [rserver] ERROR Failed to validate sign-in with invalid CSRF form; LOGGED FROM: bool rstudio::server::auth::common::validateSignIn(const rstudio::core::http::Request&, rstudio::core::http::Response*) src/cpp/server/auth/ServerAuthCommon.cpp:132

Unfortunately the compiled submit.yml.erbsubmit.yml doesn’t get placed in the job data directory or anywhere else that I can find in the user’s home directory or the ondemand server. Where do these files get compiled to?

I think ERB rendering is a red herrring here. This appears to be valid yaml and outside of anything that’s being templated, so I don’t believe the underlying issue is being caused by ERB rendering.

I’d ask if csrftoken is being populated in the job’s connection.yml and if you’re consuming & using it in the view.html.erb.

If it’s not populated in connection.yml or is empty - you need to export the variable (it is case senstivie so keep that in mind)

That’s the thing. Before I modified submit.yml.erb the csrftoken value showed correctly in connection.yml and everything worked correctly (connect button opens new tab and bypasses login screen going directly into the app).

After my modification csrftoken no longer shows in connection.yml and the automatic login, and manual fails due to the missing token.

This is why I wanted to validate the compiled submit.yml to see if I introduced a yaml error without meaning to.

OK - well i did patch this to write the submit.yml if it’s incorrectly formatted YAML. So at the current time, it appears to be valid yaml - even if it’s not the structure you want.

So, add something somewhere to make it invalid YAML and submit the job and it’ll be in the log location/submit.yml.

Although reviewing the commit - this addition is only in 3.0.x,so won’t work if you’re still on 2.0.x.

I’m currently on 3.0.3, which I think is latest, but even adding invalid yaml syntax I’m not seeing the file getting written to the session directory. Tried restarting my pun to the same affect. If I break the ERB portion I can get a red error box (form doesn’t submit) from that but the staged root gets deleted and the link (staged root directory) in the red error box doesn’t work due to that.

To somehow keep troubleshooting i’ve been looking at job_script_options.json which has some data from the compiled submit.yml. What i’ve found is that as long as template_submit is set the local submit.yml.erb gets completely replaced by the template that is being imported and converted to a hash.

I renamed some variables in case template_root, or template_submit, were reserved or had hidden functionality.

<%-
require 'yaml'
require 'open3'
c_root = File.expand_path(File.join(__dir__, '..', 'templates'))
test1 = YAML.load( ERB.new(File.read(File.join(c_root, 'test.yml.erb')), trim_mode: '-').result(binding) )
-%>
---
batch_connect:
  template: basic

script:
  queue_name: "normal"
  native:
    - "--hint"
    - "nomultithread"

Resulting job_script_options.json:

{
  "job_name": "sys/dashboard/dev/matlab-web",
  "workdir": "/fs1/home/nvonwolf/ondemand/data/sys/dashboard/batch_connect/dev/matlab-web/output/704b9a21-c1d9-4d7f-832d-4b76cb812525",
  "output_path": "/fs1/home/nvonwolf/ondemand/data/sys/dashboard/batch_connect/dev/matlab-web/output/704b9a21-c1d9-4d7f-832d-4b76cb812525/output.log",
  "shell_path": "/bin/bash",
  "wall_time": 3600,
  "native": [
    "--nodes",
    "1",
    "--ntasks",
    "1",
    "--cpus-per-task",
    "4",
    "--mem",
    "4G"
  ],
  "queue_name": "interactive"
}

Looking at the native sections you can see they don’t match at all. Commenting out test1 variable assignment, from the previous example, returns:

{
  "job_name": "sys/dashboard/dev/matlab-web",
  "workdir": "/fs1/home/nvonwolf/ondemand/data/sys/dashboard/batch_connect/dev/matlab-web/output/9379189a-cd0a-415b-b3dd-68710d5ad528",
  "output_path": "/fs1/home/nvonwolf/ondemand/data/sys/dashboard/batch_connect/dev/matlab-web/output/9379189a-cd0a-415b-b3dd-68710d5ad528/output.log",
  "shell_path": "/bin/bash",
  "wall_time": 3600,
  "native": [
    "--hint",
    "nomultithread"
  ],
  "queue_name": "normal"
}

So I’m not sure how the variable assignment is leaking though my assumption is ERB.new is somehow writing it’s content even though its being fed into YAML.load.

I’ll have to set something up to replicate. I’m finding it hard to believe that’s the behavior, but there must be something here that I’m missing.

Through a lot of trial and error, looking at job_script_options.json results, i’ve figured it out. ERB.new combined with .result(binding) was leaking and somehow causing everything else in the submit.json.erb file to be ignored.

Here is the fixed code that now works:

submit.yml.erb

<%-
require 'yaml'
require 'open3'
template_root = File.expand_path(File.join(__dir__, '..', 'templates'))
file_content = File.read(File.join(template_root, 'test.yml.erb'))
erb_content = ""
ERB.new(file_content, trim_mode: '-', eoutvar: "erb_content").result(binding)
yaml_content = YAML.load(erb_content)
-%>
---
batch_connect:
  template: basic

script:
  queue_name: <%= yaml_content['script']['queue_name'] %>
  native:
<%= YAML.dump(yaml_content['script']['native']).gsub("---\n", '').gsub(/^/,"    ") %>

    - "--hint"
    - "nomultithread"

job_script_options.json

{
  "job_name": "sys/dashboard/dev/matlab-web",
  "workdir": "/fs1/home/nvonwolf/ondemand/data/sys/dashboard/batch_connect/dev/matlab-web/output/62e4e042-61bb-46ad-98af-3121cb73abc1",
  "output_path": "/fs1/home/nvonwolf/ondemand/data/sys/dashboard/batch_connect/dev/matlab-web/output/62e4e042-61bb-46ad-98af-3121cb73abc1/output.log",
  "shell_path": "/bin/bash",
  "wall_time": 3600,
  "native": [
    "--nodes",
    "1",
    "--ntasks",
    "1",
    "--cpus-per-task",
    "4",
    "--mem",
    "4G",
    "--hint",
    "nomultithread"
  ],
  "queue_name": "interactive"
}

Looking at the native value you can see that the nomultithread flag is now showing up where previously it did not.

While I split up the ruby code to more easily troubleshoot, the fix was using eoutvar with ERB.new. This stopped the unexpected behavior and fixed my issues with both this matlab example and the initial rstudio example.

With this resolved would it still be possible to open a request to store/copy the form.yml, submit.yml, view.html, and any other related ERB results to the job session directory. This would make troubleshooting much easier in the future.

I can open up an issue in github if you would prefer.