Skip to content

Commit c2343d3

Browse files
committed
add deferred commands and relative path support for download & upload
1 parent 288579f commit c2343d3

15 files changed

Lines changed: 597 additions & 226 deletions

File tree

qDup-core/src/main/java/io/hyperfoil/tools/qdup/cmd/Cmd.java

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import io.hyperfoil.tools.yaup.StringUtil;
1414
import io.hyperfoil.tools.yaup.json.Json;
1515
import io.hyperfoil.tools.yaup.json.JsonMap;
16+
import io.hyperfoil.tools.yaup.time.SystemTimer;
1617
import org.jboss.logging.Logger;
1718

1819
import java.lang.invoke.MethodHandles;
@@ -623,6 +624,8 @@ public static String populateStateVariables(String command, Cmd cmd, State state
623624
private Cmd parent;
624625
private Cmd stateParent;
625626

627+
private List<Cmd> deferredCmd = new ArrayList<>();
628+
626629
private String idleTimer = ""+DEFAULT_IDLE_TIMER;
627630
protected boolean silent = false;
628631
private boolean stateScan = true;
@@ -646,6 +649,13 @@ protected Cmd(boolean silent) {
646649
this.stateParent = null;
647650
this.uid = uidGenerator.incrementAndGet();
648651
}
652+
653+
public boolean hasDeferredCmd(){ return !deferredCmd.isEmpty();}
654+
public void addDeferredCmd(Cmd command){
655+
deferredCmd.add(command);
656+
}
657+
public List<Cmd> getDeferredCmd() {return deferredCmd;}
658+
649659
/**
650660
* Set if this command should be scanned for state references.
651661
* @param stateScan
@@ -966,8 +976,34 @@ public Cmd getPrevious() {
966976
return rtrn;
967977
}
968978

979+
/**
980+
* returns the observed command or null if this command is not in an observing context
981+
* @return
982+
*/
983+
public Cmd getObservedCmd(){
984+
if(!isObserving()){
985+
return null;
986+
}
987+
Cmd observer = getObservingRoot();
988+
if(observer!=null && observer.hasStateParent()){
989+
return observer.getStateParent();
990+
}else{
991+
return null;
992+
}
993+
}
994+
public Cmd getObservingRoot(){
995+
Cmd current = this;
996+
while(current.hasParent()){
997+
current = current.getParent();
998+
}
999+
return current;
1000+
}
9691001
public boolean isObserving(){
970-
return !hasParent() && hasStateParent();
1002+
Cmd current = this;
1003+
while(current.hasParent()){
1004+
current = current.getParent();
1005+
}
1006+
return !current.hasParent() && current.hasStateParent();
9711007
}
9721008
public boolean hasParent(){return parent!=null;}
9731009
public Cmd getParent(){return parent;}
@@ -1126,12 +1162,35 @@ public final void doRun(String input, Context context) {
11261162

11271163
public void preRun(String input,Context context){
11281164

1165+
}
1166+
protected void runDeferred(String output, Context context){
1167+
1168+
if(hasDeferredCmd()){
1169+
if(context instanceof ScriptContext scriptContext){
1170+
SystemTimer deferredTimer = scriptContext.getContextTimer().start("deferred");
1171+
for(Cmd deferred : getDeferredCmd()){
1172+
SyncContext syncContext = new SyncContext(
1173+
scriptContext.getShell(),
1174+
scriptContext.getState(),
1175+
scriptContext.getRun(),
1176+
deferredTimer,//TODO create a deferred timer for this context?
1177+
deferred,
1178+
scriptContext
1179+
);
1180+
deferred.doRun(output,syncContext);
1181+
}
1182+
deferredTimer.stop();
1183+
}else{
1184+
//this shouldn't happen
1185+
}
1186+
}
11291187
}
11301188
public void postRun(String output,Context context){
11311189
String toLog = getLogOutput(output,context);
11321190
if(toLog != null && !toLog.isBlank()){
11331191
context.log(toLog);
11341192
}
1193+
runDeferred(output, context);
11351194
}
11361195

11371196

qDup-core/src/main/java/io/hyperfoil/tools/qdup/cmd/Context.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,6 @@ public interface Context {
4141
void close();
4242
boolean isAborted();
4343

44-
void setCwd(String dir);
45-
String getCwd();
46-
47-
void setHomeDir(String dir);
48-
String getHomeDir();
49-
5044
Globals getGlobals();
5145

5246
}

qDup-core/src/main/java/io/hyperfoil/tools/qdup/cmd/Dispatcher.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,6 @@ public Json getActiveJson(){
163163
entry.set("uid",currentCmd.getUid());
164164
entry.set("sessionId",context.getContextId());
165165
entry.set("script",rootCmd.getUid()+":"+rootCmd.toString());
166-
entry.set("cwd",context.getCwd());
167166
if(currentCmd instanceof Sh){
168167
entry.set("input",currentCmd.getPrevious()!=null?currentCmd.getPrevious().getOutput():"");
169168
entry.set("output",context.getShell().peekOutput());

qDup-core/src/main/java/io/hyperfoil/tools/qdup/cmd/ScriptContext.java

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -99,22 +99,7 @@ public Cmd copy() {
9999
long updateTime = -1;
100100

101101
private String roleName="";
102-
private String cwd="";
103-
private String homeDir="";
104102
private boolean isAborted=false;
105-
@Override
106-
public String getCwd(){return cwd;}
107-
@Override
108-
public void setCwd(String cwd){
109-
this.cwd = cwd;
110-
}
111-
112-
@Override
113-
public void setHomeDir(String dir){
114-
this.homeDir = dir;
115-
}
116-
@Override
117-
public String getHomeDir(){return homeDir;}
118103

119104
public boolean checkExitCode(){return checkExitCode;}
120105

qDup-core/src/main/java/io/hyperfoil/tools/qdup/cmd/SpyContext.java

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ public class SpyContext implements Context {
2626
private Context context;
2727
private Globals globals;
2828

29-
private String cwd="";
30-
private String homeDir="";
3129
public SpyContext(){
3230
this(null,new State(""),new Coordinator(new Globals()));
3331
}
@@ -230,25 +228,6 @@ public boolean isAborted(){
230228
return context!=null ? context.isAborted() : false;
231229
}
232230

233-
@Override
234-
public void setCwd(String dir) {
235-
this.cwd = cwd;
236-
}
237-
238-
@Override
239-
public String getCwd() {
240-
return cwd;
241-
}
242-
243-
@Override
244-
public void setHomeDir(String dir) {
245-
this.homeDir = dir;
246-
}
247-
248-
@Override
249-
public String getHomeDir() {
250-
return homeDir;
251-
}
252231
public boolean calledAbort(){return aborted;}
253232
public String getNext(){return next;}
254233
public boolean hasNext(){return next!=null;}

qDup-core/src/main/java/io/hyperfoil/tools/qdup/cmd/SyncContext.java

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ public class SyncContext implements Context, Runnable{
2929
private final ScriptContext scriptContext;
3030
private final Cmd scriptActiveCmd;
3131

32-
private String cwd="";
33-
private String homeDir="";
3432

3533
public SyncContext(AbstractShell session, State state, Run run, SystemTimer timer, Cmd currentCmd, ScriptContext scriptContext){
3634
this.session = session;
@@ -43,31 +41,9 @@ public SyncContext(AbstractShell session, State state, Run run, SystemTimer time
4341
this.scriptActiveCmd = scriptContext.getCurrentCmd();
4442
}
4543

46-
public void setCwd(String cwd){
47-
this.cwd = cwd;
48-
}
49-
50-
public String getCwd(){
51-
if(scriptContext!=null){
52-
return scriptContext.getCwd();
53-
} else {
54-
return cwd;
55-
}
56-
}
57-
5844
@Override
5945
public Globals getGlobals(){return run.getConfig().getGlobals();}
6046

61-
@Override
62-
public void setHomeDir(String dir) {
63-
this.homeDir = dir;
64-
}
65-
66-
@Override
67-
public String getHomeDir() {
68-
return homeDir;
69-
}
70-
7147
public Run getRun(){ return run;}
7248

7349
@Override
@@ -213,7 +189,7 @@ public SystemTimer getContextTimer() {
213189

214190
@Override
215191
public SystemTimer getCommandTimer() {
216-
return null;
192+
return cmdTimer;
217193
}
218194

219195
@Override

qDup-core/src/main/java/io/hyperfoil/tools/qdup/cmd/impl/Download.java

Lines changed: 66 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import io.hyperfoil.tools.qdup.cmd.Context;
77

88
import java.io.File;
9+
import java.nio.file.Paths;
910
import java.util.function.Supplier;
1011

1112
public class Download extends Cmd {
@@ -76,35 +77,77 @@ public String execute(Context context, Supplier<Local> localProvider, Supplier<H
7677
String destinationPath = context == null ? destination : populateStateVariables(basePath + File.separator +destination,this,context);
7778

7879

80+
if(context!=null && context.getShell().isActive() && (
81+
QueueDownload.hasBashEnv(remotePath) ||
82+
remotePath.startsWith("~/") ||
83+
!remotePath.startsWith("/")
84+
)
85+
){
86+
if(isObserving()){
87+
getObservedCmd().addDeferredCmd(
88+
new Download(remotePath,populateStateVariables(destination,this,context),maxSize)
89+
);
90+
return destinationPath;
91+
}else{
92+
logger.error("context is busy and not observing when running "+this);
93+
}
94+
}else{
95+
96+
if(context==null && (
97+
QueueDownload.hasBashEnv(remotePath) ||
98+
remotePath.startsWith("~/") ||
99+
!remotePath.startsWith("/")
100+
)){
101+
//This is an error condition, abort
102+
logger.error("cannot download "+remotePath+" without a connection to resolve the full path");
103+
return null;
104+
}
79105

80-
File destinationFile = new File(destinationPath);
81-
if(!destinationFile.exists()){
82-
destinationFile.mkdirs();
83-
}
84-
boolean canDownload = true;
85-
if(maxSize != null){
86-
Long remoteFileSize = local.remoteFileSize(remotePath,host);
87-
if(remoteFileSize > maxSize){
88-
canDownload = false;
89-
logger.warnf("Download File: `%s`; is larger %s than max size: %s bytes", remotePath, remoteFileSize, maxSize);
106+
if(QueueDownload.hasBashEnv(remotePath)){
107+
remotePath = context.getShell().shSync("export __qdup_ec=$?; echo "+remotePath+"; (exit $__qdup_ec)");
90108
}
91-
}
92-
if(canDownload) {
93-
boolean worked = local.download(remotePath, destinationPath, host);
94-
if(!worked){
95-
if(context!=null) {
96-
context.error("failed to download " + remotePath + " to " + destinationPath);
97-
context.abort(false);
109+
if(remotePath.startsWith("~/")){
110+
//export __qdup_ec=$?; echo "${__qdup_ec}"; (exit $__qdup_ec)
111+
String homeDir = context.getShell().shSync("export __qdup_ec=$?; echo ~/; (exit $__qdup_ec)");
112+
remotePath = homeDir+remotePath.substring("~/".length());
113+
}else if (!remotePath.startsWith("/")){
114+
String pwd = context.getShell().shSync("export __qdup_ec=$?; pwd; (exit $__qdup_ec)");
115+
String normalized = Paths.get(pwd,remotePath).toAbsolutePath().normalize().toString();
116+
if(remotePath.endsWith("/") && !normalized.endsWith("/")){
117+
normalized = normalized + "/";
98118
}
99-
return null;
119+
remotePath = normalized;
100120
}
101121

122+
File destinationFile = new File(destinationPath);
123+
if(!destinationFile.exists()){
124+
destinationFile.mkdirs();
125+
}
126+
boolean canDownload = true;
127+
if(maxSize != null){
128+
Long remoteFileSize = local.remoteFileSize(remotePath,host);
129+
if(remoteFileSize > maxSize){
130+
canDownload = false;
131+
logger.warnf("Download File: `%s`; is larger %s than max size: %s bytes", remotePath, remoteFileSize, maxSize);
132+
}
133+
}
134+
if(canDownload) {
135+
boolean worked = local.download(remotePath, destinationPath, host);
136+
if(!worked){
137+
if(context!=null) {
138+
context.error("failed to download " + remotePath + " to " + destinationPath);
139+
context.abort(false);
140+
}
141+
return null;
142+
}
143+
}
144+
return canDownload ?
145+
destinationPath.endsWith(File.separator) ?
146+
destinationPath + (new File(remotePath)).getName() :
147+
destinationPath + File.separator + (new File(remotePath)).getName()
148+
: null;
102149
}
103-
return canDownload ?
104-
destinationPath.endsWith(File.separator) ?
105-
destinationPath + (new File(remotePath)).getName() :
106-
destinationPath + File.separator + (new File(remotePath)).getName()
107-
: null;
150+
return null; // something went wrong
108151
}
109152

110153
}

0 commit comments

Comments
 (0)