Synthetic File Download by Link: Tableau Issue

We are using tableau dataviz module inside a website. User clicks an export link, selects format, clicks download to generate file, then clicks download once more in a new popup. It is this final download button that has an href that is not a pointer to the actual file, but a link that provides parameters to Tableau to locate and download the file. An example: https://domain.com/vizql/t/section/w/BenchmarkSummary_tabbed/v/BenchmarkSummary/tempfile/sessions/A76FA6C8029B4835AC2DF27319566EBD-2:1/?key=152086064&keepfile=yes&attachment=yes
Notice the session and key sections of the link.

It doesn’t seem to matter if i click the button or $browser.get the href, the file does not download to the private minion. I have worked with support and identified the download directory in the container does not contain the file (docker exec -it minionName ls ) The default download directory is /opt/user. Additionally, I use this block of code to attempt to read the file and perform MD5 hash. The return error is no file found.

       .then(function(){
        console.log("hashing download file");
        var downloadedFile = fs.readFileSync("/opt/user/_Benchmark Summary.pdf");       
        var hash = md5sum.update(downloadedFile).digest('hex');
        console.log(hash); 
})

I have used SE IDE and Kalaton to record the function and it looks like a new browser window is opened when the link is clicked and then closed once the download begins. This new window is not visible when manually downloading from my PC.
Any thoughts on why the file is not downloading?

Hey @klynch,

Thanks for posting your findings here in Explorers Hub! This seems like an issue many people could face.

To be clear, did you say this example download script does or does not work with the tableau download button?
https://newrelic.github.io/quickstarts-synthetics-library/#/view/FileDownload

One thing to note about how we process scripted browser jobs, they don’t get executed on the minion container. Instead the minion spawns a “runner” container to process them, one runner per non-ping job.

Since the runners are ephemeral, any files stored therein will be deleted when the job is done. If the download happens outside the flow of the script, i.e. a new Chrome window pops up and does the download asynchronously, it’s possible the container could be deleted before the download can take place. You might try using the $browser.waitForAndFindElement function if not already.
https://docs.newrelic.com/docs/synthetics/synthetic-monitoring/scripting-monitors/scripted-browser-examples/#wait-for-element

It’s not well documented, but if you inspect the runner container you will see the volume mounts it has. The following command should watch for a runner and then exit after it notices one, saving the results to a file.

$ watch -g 'docker inspect $(docker ps -q --filter "label=name=synthetics-minion-runner" | head -1) 2>/dev/null | tee runner-inspect.yaml'

Then grep for the bind mounts:

$ grep -A3 Binds runner-inspect.yaml 
            "Binds": [
                "/tmp/shared-volume-4458638883687341279:/opt/shared:rw",
                "/dev/shm:/dev/shm"
            ],

The runner containers utilize an /opt/shared directory which mounts a directory on the host in /tmp/shared-volume-<num>. Any files related to the job’s execution will be saved there including:

  • browser.log
  • chromedriver.log
  • openbox.log
  • performance.log
  • performanceMetrics.json
  • script.js
  • script.log
  • user.json
  • Xvfb.log

The purpose of the mount is so the minion can generate the HAR file and send results and logs to New Relic. Then the directory is deleted. This all happens very fast so they can be hard to catch, so it is best to add a sleep to the very end of the monitor script to give us humans a chance to read it.

// sleep for 20 seconds to check results
$browser.sleep(20000);

If you were to save the downloaded file to /opt/shared, it will show up there as well. It’s a bit easier to watch since it’s a directory on the host and doesn’t require trying to exec into an ephemeral container.

$ watch -n 1 'ls /tmp/shared-volume-*'

It’s also possible to watch the action inside the runner container with a longer command:

$ watch -n 1 'docker exec -it $(docker ps -q --filter "label=name=synthetics-minion-runner" | head -1) 2>/dev/null ls /opt/shared'

@kmullaney thank you for this information. No, the file download quick start does not work in this case. I will investigate further using the information you have provided and will post back soon.

@kmullaney using your information i was able to inspect the chromedriver.log and browser log. I think the issue lies in this error found in the browser log:
Object",“timestamp”:1645021503790,“type”:""},{“level”:“SEVERE”,“message”:“https://analystcorner.stagebi.nationalresearch.com/vizql/v_202032101101657/javascripts/runtimeanimweb.js 0 [Report Only] Refused to compile or instantiate WebAssembly module because ‘wasm-eval’ is not an allowed source of script in the following Content Security Policy directive: “script-src * blob:”.\n”,“timestamp”:1645021507406,“type”:""}]

I found this post from a tableau developer but there doesn’t seem to be a resolution:

Can you add some insight into the error condition as it relates to chrome in CPM?

thanks

Hey @klynch,

I asked our Synthetics Engineering Manager and they confirmed we do indeed block WebAssembly in order to prevent misuse of our Synthetics monitors.

That certainly explains why the download isn’t working. Nice work finding that error in the chromedriver.log though! Perhaps this thread can help someone who needs to inspect the logs generated by Chrome and the runner during a job execution.

2 Likes

@kmullaney thank you for the information you have provided. I was wondering if I could get you to ask the Engineering Manager if it is possible to change this behavior for a private minion. If so, perhaps provide some steps to complete.

Hello @klynch -

Thanks for your patience with this. Had a chat with our teams about this. It’s actually a pretty complicated request to try to change the private minion behavior and it’s not something that we can just provide steps for. But, I did bring it to their attention and if we see that there’s a broader interest in this they will revisit the topic.

Really appreciate you raising this interesting use case with us!