Repository navigation
feat: add the sasviya runtime - #462
Conversation
RUN_TIMES=sasviya executes stored programs on a remote SAS Viya compute server instead of a local SAS binary. The server mints an access token from VIYA_USER/VIYA_PASSWORD against the sas.cli public client, submits the program to a compute session on VIYA_CONTEXT, and relays the session log and the program's _webout content back into the session folder, so the STP response contract is unchanged. The transport is @sasjs/adapter/node, which already implements the hot session pool, code submission, log relay and _webout retrieval. Every run uses one service account in this release; per-user Viya identities need a token per user and a session pool that cannot be shared across identities. Programs respond with %mv_webout, which must be present on the Viya estate (it ships with a SASjs deployment). The runtime is selected by the .sasviya extension.
RunTimeType is part of the generated API contract (ExecuteCodePayload.runTime), so adding the sasviya member changes public/swagger.yaml. swagger.spec.ts guards the committed file against the controllers and caught the drift.
One sentence per line, so a renderer wraps the text rather than the file carrying hard line breaks.
The session lifecycle lived on SASSessionController, so only the local sas runtime ever destroyed a session. A triggered program on js, py, r or the new sasviya runtime left its session folder behind permanently and the documented expiresAfterMins had no effect on it. Move deleteSession and scheduleSessionDestroy onto the base SessionController and schedule the destroy from the base createSession, so every runtime gets the same lifecycle the local sas runtime already had. Found by triggering a long-running job: 15 session folders had accumulated, and a session triggered with expiresAfterMins=1 was still on disk minutes after it completed.
|
Follow-up commit Triggering a long-running job ( Cause: Covered by Full API suite on |
npm audit now fails on braces (GHSA-vfj7-8cjw-p6xm), which has no patched release - the advisory covers every published version. The only reachable path is dev-only: the watcher -> chokidar -> braces. Nothing in the shipped image uses the watcher, so the path goes rather than the audit being narrowed. node --watch replaces it for the development start script, and ts-node/register is already a devDependency. The watcher's config block is no longer needed: watch mode only restarts on files in the module graph, and sasjs_root is not one of them. Verified: the dev server boots on the new command, a change to a loaded source file restarts it, and npm audit --audit-level=low reports 0 vulnerabilities (18 packages removed).
|
Two follow-up commits, and the remaining CI picture.
Every other job is green on Async trigger evidence, on a 12-second |
There was a problem hiding this comment.
Reviewed the full base...head diff (13 files: viyaClient, createViyaSASProgram, processProgram sasviya branch, session-destroy move to the base controller, env validation, swagger enum, docs, api lockfile) plus the check status at this head.
- .github/workflows/build.yml:66 - the security job is red at this head. The web audit (npm audit --audit-level=high) reports 18 high advisories (braces GHSA-vfj7-8cjw-p6xm reached through sass, ts-loader, webpack-dev-server and http-proxy-middleware) plus the 2 known moderate react-router ones. None of it comes from this PR: it touches api/ only, the root and api audits pass, and the same job was green at commit 7a16ac4 and red at 5aff50e with no web dependency change in between, so a newly published advisory is what flipped it. The gate stays red on every push until the web dev-dependency bumps land; sequence that work (or a documented override) before merging so the merge commit is not born red.
- api/src/controllers/internal/viyaClient.ts:33 - resetViyaAuth is exported but nothing calls it outside the jest mock. If the cached token set ever becomes unusable (access token expired and the single-use refresh already spent), every subsequent request fails until the process restarts. Either wire it into the failure path (catch the adapter's auth errors, reset the cache, retry once) or drop the export.
2 findings above for review.
|
🎉 This PR is included in version 1.20.0 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
Closes #461.
What
RUN_TIMES=sasviyaexecutes stored programs on a remote SAS Viya compute server instead of a local SAS binary. The server mints an access token fromVIYA_USER/VIYA_PASSWORDagainst the built-insas.clipublic client, submits the program to a compute session onVIYA_CONTEXT, and relays the session log and the program's_weboutcontent back into the server's session folder - soExecutionControllerand the STP response contract are unchanged.The transport is
@sasjs/adapter/node, which already implements the hot-session pool, code submission, log relay and_weboutretrieval.Why
On an estate where Viya accepts connections only from the server's IP, the server is the only host with a network path to Viya. This makes it the single egress point, so browsers and CI pipelines need only a server token - no direct Viya network access and no Viya credentials in a pipeline. Everything the server already provides (auth, groups, the Drive, session-state polling, streaming, the webout contract) then applies unchanged to Viya execution.
Changes
RunTimeType.SASVIYAand startup validation ofVIYA_URL,VIYA_USER,VIYA_PASSWORDand optionalVIYA_CONTEXT(defaultCompute Reusable) inverifyEnvVariables.ts.viyaClient.ts- token mint, compute execution, log and webout relay, token rotation.createViyaSASProgram.ts- a preamble that does not inject the local_weboutfileref,SASAUTOSor header paths.SASVIYAbranch inprocessProgram.ts.@sasjs/adapteradded toapi/package.json; lockfile regenerated with npm 10.api/public/swagger.yamlregenerated -RunTimeTypeis part of the generated contract (ExecuteCodePayload.runTime), andswagger.spec.tscaught the drift.Session.ts- the session lifecycle (deleteSession/scheduleSessionDestroy) moves fromSASSessionControllerto the baseSessionController, and the basecreateSessionschedules the destroy. Only the localsasruntime ever destroyed a session, so a triggered program onjs,py,rorsasviyaleft its session folder behind permanently andexpiresAfterMinshad no effect on it. Found while testing the trigger path: 15 session folders had accumulated and a session triggered withexpiresAfterMins=1was still on disk minutes after completing.docs/viya-runtime.md- the design note (also at https://paste.4gl.io/?e78a7d66e2e85825#DypKnyHz6poHsMViN1DwfqLJyFcjNHRzgNe1LhNsCDQi).Verification
Unit:
api/src/controllers/internal/spec/viyaRuntime.spec.ts,sessionLifecycle.spec.ts, plus the Viya cases inapi/src/utils/specs/verifyEnvVariables.spec.ts. Full API suite: 39 suites / 497 tests green.tsc --noEmitand prettier 3.9.9 clean.CLI end to end:
sasjs compile -t viya->sasjs build->sasjs build -t server->sasjs deploy -t serveragainst a local server runningRUN_TIMES=js,sasviya.Live Viya, through the server:
The
_debugresponse carries the Viya session log, proving execution happened on the compute server and was relayed back.Async trigger, on a 12-second program on the
jsruntime:CI status
All jobs pass except
security, which fails on thebracesadvisoryGHSA-vfj7-8cjw-p6xm(stack-exhaustion DoS). That advisory now covers every publishedbracesversion - the registry reportsfirst_patched_version: None- so no bump fixes it.bracesvianodemon->chokidar. Commit098e333removes that path (dev-only; the shipped image runs the built server) and replaces it withnode --watch.npm audit --audit-level=lowinapinow reports 0 vulnerabilities.bracesadvisory, reached throughmicromatch(eslint-webpack-plugin, ts-loader, http-proxy-middleware, fast-glob) andchokidar(sass).web/is byte-identical toorigin/mainin this PR (git diff origin/main HEAD -- web/is empty), andnpm audit --audit-level=highinwebreports the same 20 locally. This is repo-wide and pre-existing, not introduced here.bracesis build-time tooling only - it is not in the deployed image - but there is no patched release to move to, so unblocking thesecurityjob needs a decision: wait for abracesrelease, add a documented dated exception for that one advisory, or migrate the web toolchain off the CRA-era stack.Known limits
%mv_weboutmust exist on the estate (it ships with a SASjs deployment). The server does not inject the macros..sasviyaextension;.sasstill means the local runtime.sasViyaApiClientbecause the publicSASjs.executeScriptdoes not return the_weboutfileref; a public option there is a candidate follow-up insasjs/adapter.