The self-destruct block ends with a single-statement %else in open code:
%if &keepme ne 1 %then %do;
%mv_deletejes(path=&root/admin, name=makedata);
%end;
%else %put NOTE: &_program retained, as KEEPME was requested;
Outside a macro SAS pairs %else only with a %do branch, so this stops with
'ERROR: Expected %DO not found' and 'ERROR: Skipping to next %END statement'
after the deployment has completed. On Viya the job then reports state error
with jobConditionCode 1012, so a successful deployment looks like a failure
exactly when someone is watching a first install.
Both branches now use %do/%end, which is the pattern the repo uses elsewhere
for this reason - see db/migrations/20261003_row_and_cell_limits.sas.
The same block is fixed in the server variant. The two delete calls also gain
their terminating semicolons: the macro call worked without them, but their
absence is what made the block look responsible for the error.
Behaviour is unchanged: the program still deletes itself unless KEEPME was
requested.
306 lines
7.8 KiB
SAS
306 lines
7.8 KiB
SAS
/**
|
|
@file
|
|
@brief self destructing setup service
|
|
@details Will create the database and perform config activities
|
|
|
|
<h4> SAS Macros </h4>
|
|
@li mf_getapploc.sas
|
|
@li mf_getuniquefileref.sas
|
|
@li mf_mkdir.sas
|
|
@li mf_trimstr.sas
|
|
@li mp_abort.sas
|
|
@li mpe_makedata.sas
|
|
@li mpe_makedatamodel.sas
|
|
@li mpe_makesampledata.sas
|
|
@li mv_createfile.sas
|
|
@li mv_deletejes.sas
|
|
|
|
@version 3.5
|
|
@author 4GL Apps Ltd
|
|
@copyright 4GL Apps Ltd. This code may only be used within Data Controller
|
|
and may not be re-distributed or re-sold without the express permission of
|
|
4GL Apps Ltd.
|
|
|
|
**/
|
|
|
|
|
|
%global dcpath ADMIN _contextname ;
|
|
|
|
%webout(FETCH)
|
|
|
|
/* enable vars to be passed as url params */
|
|
%let exist=%sysfunc(exist(work.fromjs));
|
|
%let inds=%sysfunc(ifc(&exist=1,fromjs,_null_));
|
|
|
|
/* optional KEEPME column - keep this program on the content server after a
|
|
* successful run. A normal install self-destructs so the seeding cannot be
|
|
* run twice over live data; a test harness reseeds once and then re-runs
|
|
* individual tests, so it wants the program to stay put. */
|
|
%let keepme=0;
|
|
%if &exist=1 %then %do;
|
|
data _null_;
|
|
dsid=open('work.fromjs');
|
|
/* every row, not just the first: KEEPME may be set on any of them */
|
|
if varnum(dsid,'KEEPME')>0 then do until(eof);
|
|
set work.fromjs end=eof;
|
|
if upcase(cats(keepme)) in ('1','Y','YES','TRUE') then call symputx('keepme',1);
|
|
end;
|
|
rc=close(dsid);
|
|
stop;
|
|
run;
|
|
%end;
|
|
|
|
data _null_;
|
|
set &inds;
|
|
call symputx('dcpath',dcpath);
|
|
call symputx('ADMIN',ADMIN);
|
|
call symputx('_contextname',_contextname);
|
|
run;
|
|
|
|
|
|
options noquotelenmax;
|
|
|
|
%let dclib=%upcase(VIYA%substr(%sysevalf(%sysfunc(datetime())/60),3,4));
|
|
%let dclibname=Data Controller (&dclib);
|
|
%let DC_LIBREF=&dclib;
|
|
%let work=%sysfunc(pathname(work));
|
|
|
|
|
|
%let dcpath=%mf_trimstr(&dcpath,/)/&dclib;
|
|
%put &=sysuserid;
|
|
%put &=dcpath;
|
|
%put &=admin;
|
|
|
|
%mf_mkdir(&dcpath)
|
|
%mp_abort(iftrue= (&syscc ne 0)
|
|
,mac=&_program
|
|
,msg=%str(Unable to create &dcpath using &sysuserid)
|
|
)
|
|
%mf_mkdir(&dcpath/secret)
|
|
%mf_mkdir(&dcpath/dc_staging)
|
|
|
|
/* check we have physical permissions to the DCLIB folder */
|
|
data _null_;
|
|
putlog "dcpath=&dcpath/permTest.txt";
|
|
putlog "sysuserid=&sysuserid";
|
|
data _null_;
|
|
file "&dcpath/permTest.txt";
|
|
run;
|
|
%mp_abort(iftrue= (&syscc ne 0)
|
|
,mac=&_program
|
|
,msg=%str(User &sysuserid does not have WRITE permissions to: &dcpath )
|
|
)
|
|
filename delfile "&dcpath/permTest.txt";
|
|
data _null_;
|
|
rc=fdelete('delfile');
|
|
run;
|
|
%mp_abort(iftrue= (&syscc ne 0)
|
|
,mac=&_program..sas
|
|
,msg=%str(User &sysuserid could create (but not delete) &dcpath/permTest.txt )
|
|
)
|
|
|
|
|
|
libname &dclib "&dcpath";
|
|
%global admin;
|
|
%let admin=%sysfunc(coalescec(&admin,All Users));
|
|
%mpe_makedatamodel(lib=&dclib)
|
|
%mpe_makedata(lib=&dclib,mpeadmins=&admin,path=%str(&dcpath))
|
|
|
|
%mp_abort(iftrue=(&syscc ne 0)
|
|
,mac=&sysmacroname
|
|
,msg=%str(Err during &dclib build)
|
|
)
|
|
|
|
|
|
/* sample data library */
|
|
%mf_mkdir(&dcpath/dc_demo)
|
|
libname dcdemo "&dcpath/dc_demo";
|
|
%mpe_makesampledata(outlib=DCDEMO)
|
|
|
|
%mp_abort(iftrue=(&syscc ne 0)
|
|
,mac=&sysmacroname
|
|
,msg=%str(Err during demo data build)
|
|
)
|
|
|
|
|
|
/* the DC precode is stored in the root of the project */
|
|
%let root=%mf_getapploc(&_program)/services;
|
|
%put &=root;
|
|
|
|
filename jobout filesrvc folderpath="&root";
|
|
data _null_;
|
|
file jobout('settings.sas');
|
|
put '/* these values are ignored if DC_LIBREF was declared in autoexec */';
|
|
put ' ';
|
|
put '%global DC_LIBREF dc_admin_group dc_staging_area ;';
|
|
put '/* This library (libref) contains the control datasets for DC */';
|
|
put '/* If a different libref must be used, configure it below */';
|
|
put '%let DC_LIBREF=' "&dclib;";
|
|
put ' ';
|
|
put "libname &dclib '&dcpath' ;";
|
|
put ' ';
|
|
put '/* This group has unrestricted access to Data Controller */';
|
|
put '%let dc_admin_group=' "&admin;";
|
|
put ' ';
|
|
put '/* This physical location is used for staging data and audit history */';
|
|
put '%let dc_staging_area=' "&dcpath/dc_staging;";
|
|
put ' ';
|
|
put 'cas dcsession sessopts=(caslib=casuser);';
|
|
put 'caslib _all_ assign;';
|
|
put ' ';
|
|
if &syssite in (70221618,70253615) then do;
|
|
put "libname dcdemo '&dcpath/dc_demo';";
|
|
end;
|
|
run;
|
|
|
|
|
|
/* create demo data
|
|
|
|
cas dcsession;
|
|
caslib _all_ assign;
|
|
caslib casmusic path='/opt/sas/viya/cascache/tracks' libref=casmusic GLOBAL;
|
|
|
|
proc casutil;
|
|
LOAD DATA=dcdemo.cars
|
|
CASOUT="cars"
|
|
OUTCASLIB="casmusic" PROMOTE ;
|
|
run;
|
|
*/
|
|
|
|
/*
|
|
cas mysess;
|
|
caslib _all_ assign;
|
|
data casmusic.artists(promote=yes);
|
|
length name varchar(30);
|
|
do tracks=1 to 100;
|
|
name='Phil Collins'!!cats(tracks);
|
|
output;
|
|
end;
|
|
run;
|
|
*/
|
|
/*
|
|
%let url=http://millionsongdataset.com/sites/default/files/AdditionalFiles%trim(
|
|
)/unique_tracks.txt;
|
|
filename test url "&url" lrecl=3000 ;
|
|
|
|
proc sql;
|
|
drop table casmusic.tunes;
|
|
|
|
data tracks;
|
|
infile test dlmstr='<SEP>' dsd end=lastobs;
|
|
input track_id:$32. song_id:$32. artist_nm:$128. title:$256.;
|
|
output;
|
|
if lastobs then do;
|
|
track_id='dummyrecords';
|
|
title='none';
|
|
artist_nm='none';
|
|
do x=1 to 4000000;
|
|
drop x;
|
|
song_id=cats(x);
|
|
output;
|
|
end;
|
|
stop;
|
|
end;
|
|
run;
|
|
|
|
proc casutil;
|
|
LOAD DATA=tracks
|
|
CASOUT="tunes"
|
|
OUTCASLIB="casmusic" PROMOTE ;
|
|
run;
|
|
/*
|
|
data append;
|
|
if 0 then set &dclib..MPE_TABLES;
|
|
libref="CASMUSIC";
|
|
dsn='TUNES';
|
|
num_of_approvals_required=1;
|
|
loadtype='UPDATE';
|
|
buskey='TRACK_ID SONG_ID';
|
|
tx_from=0;
|
|
tx_to='31DEC9999:23:59:59'dt;
|
|
output;
|
|
dsn='ARTISTS';
|
|
buskey='NAME';
|
|
output;
|
|
run;
|
|
proc append base=&dclib..MPE_tABLES data=append;
|
|
run;
|
|
*/
|
|
%mp_abort(iftrue=(&syscc ne 0)
|
|
,mac=&sysmacroname
|
|
,msg=%str(Err during settings job creation)
|
|
)
|
|
|
|
/* Update the compute context in the streamed frontend (DC.html) so that
|
|
Data Controller runs under the same context that was selected when this
|
|
service was invoked. The file must be re-created using %mv_createfile
|
|
(the same macro used to stream the frontend at deploy time) rather than
|
|
updated in place with a filesrvc fileref - only the Files service POST
|
|
assigns the correct mime type (text/html / file_html), without which the
|
|
frontend will not stream. This service is always invoked from a URL so
|
|
the _contextname parameter is always present. */
|
|
%global _contextname;
|
|
%let dchtml_in=%mf_getuniquefileref();
|
|
%let dchtml_out=%mf_getuniquefileref();
|
|
|
|
/* Escape regex metacharacters (\ and $) in the context name so they are
|
|
treated as literals when embedded in the prxchange replacement string.
|
|
In a Perl regex replacement, \ introduces a backreference and $ introduces
|
|
variable interpolation; prefixing each with \ quotes them as literals. */
|
|
data _null_;
|
|
length _ctxname $256 _ctxesc $512;
|
|
_ctxname=symget('_contextname');
|
|
_ctxesc=tranwrd(tranwrd(_ctxname,'\','\\'),'$','\$');
|
|
call symputx('_contextname_escaped',_ctxesc);
|
|
run;
|
|
|
|
/* read the existing frontend */
|
|
filename &dchtml_in filesrvc folderpath="&root" filename="DC.html"
|
|
lrecl=32767;
|
|
%mp_abort(iftrue= (&syscc ne 0)
|
|
,mac=&sysmacroname
|
|
,msg=%str(Unable to open DC.html in &root)
|
|
)
|
|
|
|
/* write the updated html to a temporary file */
|
|
data _null_;
|
|
file &dchtml_out lrecl=32767;
|
|
infile &dchtml_in lrecl=32767 truncover;
|
|
input;
|
|
length line $32767;
|
|
line=prxchange(cats('s|contextname="[^"]*"|contextname="'
|
|
,symget('_contextname_escaped')
|
|
,'"|'
|
|
),-1,_infile_);
|
|
put line;
|
|
run;
|
|
filename &dchtml_in clear;
|
|
|
|
%mp_abort(iftrue= (&syscc ne 0)
|
|
,mac=&sysmacroname
|
|
,msg=%str(Unable to update compute context in DC.html)
|
|
)
|
|
|
|
/* re-create DC.html via the Files service to retain the html mime type */
|
|
%mv_createfile(path=&root, name=DC.html, inref=&dchtml_out)
|
|
filename &dchtml_out clear;
|
|
|
|
|
|
%webout(OPEN)
|
|
data result;
|
|
dclib="&dclib";
|
|
admingroup="&admin";
|
|
dcpath="&dcpath";
|
|
run;
|
|
%webout(OBJ,result)
|
|
%webout(CLOSE)
|
|
|
|
/* keep the program when KEEPME was requested, so a reseed does not need a
|
|
* redeploy first */
|
|
%if &keepme ne 1 %then %do;
|
|
%mv_deletejes(path=&root/admin, name=makedata);
|
|
%end;
|
|
%else %do;
|
|
%put NOTE: &_program retained, as KEEPME was requested;
|
|
%end;
|