package org.jenkinsci.plugins.p4.tasks; import hudson.AbortException; import hudson.EnvVars; import hudson.FilePath; import hudson.model.Run; import hudson.model.TaskListener; import org.jenkinsci.plugins.p4.client.ClientHelper; import org.jenkinsci.plugins.p4.client.ConnectionHelper; import org.jenkinsci.plugins.p4.credentials.P4BaseCredentials; import org.jenkinsci.plugins.p4.review.ReviewProp; import org.jenkinsci.plugins.p4.workspace.TemplateWorkspaceImpl; import org.jenkinsci.plugins.p4.workspace.Workspace; import java.io.IOException; import java.io.Serializable; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; public abstract class AbstractTask implements Serializable { private static final long serialVersionUID = 1L; private static Logger logger = Logger.getLogger(AbstractTask.class.getName()); private P4BaseCredentials credential; private TaskListener listener; private String client; private String charset; transient private Workspace workspace; /** * Implements the Perforce task to retry if necessary * * @param p4 Perforce connection helper * @return Task object * @throws Exception push up stack */ public abstract Object task(ClientHelper p4) throws Exception; public P4BaseCredentials getCredential() { return credential; } public void setCredential(String credential) { this.credential = ConnectionHelper.findCredential(credential); } public TaskListener getListener() { return listener; } public void setListener(TaskListener listener) { this.listener = listener; } public void setWorkspace(Workspace workspace) throws AbortException { this.workspace = workspace; this.client = workspace.getFullName(); this.charset = workspace.getCharset(); // setup the client workspace to use for the build. ClientHelper p4 = getConnection(); // Check connection (might be on remote slave) if (!checkConnection(p4)) { String err = "P4: Abort, no server connection.\n"; logger.severe(err); p4.log(err); throw new AbortException(err); } // Set the client try { p4.setClient(workspace); p4.log("... client: " + getClient()); } catch (Exception e) { String err = "P4: Unable to setup workspace: " + e; logger.severe(err); p4.log(err); throw new AbortException(err); } finally { p4.disconnect(); } } public Workspace setEnvironment(Run<?, ?> run, Workspace wsType, FilePath buildWorkspace) throws IOException, InterruptedException { Workspace ws = (Workspace) wsType.clone(); // Set environment EnvVars envVars = run.getEnvironment(listener); envVars.put("NODE_NAME", envVars.get("NODE_NAME", "master")); ws.setExpand(envVars); // Set workspace root (check for parallel execution) String root = buildWorkspace.getRemote(); if (root.contains("@")) { root = root.replace("@", "%40"); String client = ws.getFullName(); String name = buildWorkspace.getName(); String[] parts = name.split("@"); String exec = parts[1]; // Update Workspace before cloning setWorkspace(ws); // Template workspace to .cloneN (where N is the @ number) String charset = ws.getCharset(); boolean pin = ws.isPinHost(); String template = client + ".clone" + exec; ws = new TemplateWorkspaceImpl(charset, pin, client, template); ws.setExpand(envVars); } ws.setRootPath(root); if (ws.isPinHost()) { String hostname = getHostName(buildWorkspace); ws.setHostName(hostname); } else { ws.setHostName(""); } return ws; } public Workspace setNextChange(Workspace ws, List<Integer> changes) { // Set label for changes to build if (changes != null) { if (!changes.isEmpty()) { String label = Integer.toString(changes.get(0)); ws.getExpand().set(ReviewProp.LABEL.toString(), label); } } return ws; } /** * Remote execute to find hostname. * * @param buildWorkspace Jenkins remote path * @return Hostname */ private static String getHostName(FilePath buildWorkspace) { try { HostnameTask task = new HostnameTask(); String hostname = buildWorkspace.act(task); return hostname; } catch (Exception e) { return ""; } } protected String getClient() { return client; } protected Workspace getWorkspace() { return workspace; } protected ClientHelper getConnection() { ClientHelper p4 = new ClientHelper(credential, listener, client, charset); return p4; } protected boolean checkConnection(ClientHelper p4) { p4.log("\nP4 Task: establishing connection."); // test server connection if (!p4.isConnected()) { p4.log("P4: Server connection error: " + getCredential().getP4port()); return false; } p4.log("... server: " + getCredential().getP4port()); // test node hostname String host; try { host = InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException e) { host = "unknown"; } p4.log("... node: " + host); return true; } protected Object tryTask() throws AbortException { ClientHelper p4 = getConnection(); if (p4.hasAborted()) { String msg = "P4: Previous Task Aborted!"; logger.warning(msg); p4.log(msg); p4.disconnect(); throw new AbortException(msg); } // Check connection (might be on remote slave) if (!checkConnection(p4)) { String msg = "\nP4 Task: Unable to connect."; logger.warning(msg); p4.log(msg); throw new AbortException(msg); } int trys = 0; int attempt = p4.getRetry(); Exception last = null; while (trys <= attempt) { trys++; try { Object result = task(p4); p4.disconnect(); if (p4.hasAborted()) { String msg = "P4: Task Aborted!"; logger.warning(msg); p4.log(msg); throw new AbortException(msg); } return result; } catch (AbortException e) { throw e; } catch (Exception e) { last = e; String msg = "P4 Task: attempt: " + trys; logger.severe(msg); p4.log(msg); // back off n^2 seconds, before retry try { TimeUnit.SECONDS.sleep(trys ^ 2); } catch (InterruptedException e2) { Thread.currentThread().interrupt(); } } } p4.disconnect(); String msg = "P4 Task: failed: " + last; last.printStackTrace(); logger.warning(msg); p4.log(msg); throw new AbortException(msg); } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#37 | 28018 | Paul Allen |
Workspace formatting for Groovy tasks. Allow the replacement of "@" in the Workspace root when running a Groovy Task. |
||
#36 | 26640 | Paul Allen |
Tick interval message to prevent timeout. An advanced setting in the Credential sets a 'tick' interval to print out a 'waiting' message to the log during long running commands. The value needs to be less than 30,000ms to avoid Jenkins automatic disconnect. JENKINS-58161 |
||
#35 | 26364 | Paul Allen |
Merge pull request #118 from williambrode/InitCauseAbortExecption. Initialize the cause of AbortException |
||
#34 | 26236 | matthew_smeeth | Added extra debugging for executor number. | ||
#33 | 26096 | Paul Allen |
Merge pull request #108 from joel-f-brown/master Credentials handling for Folders when using P4Groovy. Use run to determine the credentials in GetP4Task, then pass the P4BaseCredentials instead of the credentials ID. Now the P4Groovy getConnection() method uses the P4BaseCredentials instead of looking up the credentials from the active Jenkins instance. JENKINS-58745 JENKINS-57314 |
||
#32 | 25018 | Paul Allen |
Merge pull request #88 from p4charu/jenkinsci-master. Fixed JENKINS-55430 and better credential error reporting |
||
#31 | 24944 | Paul Allen |
Merge pull request #86 from p4charu/jenkinsci-master JENKINS-51632 and minor code cleanup |
||
#30 | 24942 | Paul Allen |
Deep clone of Workspace objects. WorkspaceSpec was not cloned and caused the View to change. JENKINS-54695 |
||
#29 | 24487 | Paul Allen | Perforce Connection Refactor. | ||
#28 | 24409 | Paul Allen | Added ChangeView and Client Backup support. | ||
#27 | 23192 | Paul Allen | Workaround for EXECUTOR_NUMBER not being set. | ||
#26 | 23190 | Paul Allen |
Merge pull request #62 from fbyrne/standardize-nodename-var. Standardize the evaluation of what NODE_NAME in p4 workspace name generation |
||
#25 | 23155 | Paul Allen |
Remove Workspace cloning for better concurrent builds. Remove Workspace cloning and encourage users to make use of EXECUTOR_NUMBER in the Jenkins job name. JENKINS-41432 |
||
#24 | 22322 | Paul Allen |
Option to disable clone for StaticSyncImpl JENKINS-43281 |
||
#23 | 22043 | Paul Allen |
Access remote Channel to find NODE_NAME If NODE_NAME is not set in the environment, look at the remote channel, then default to 'master'. JENKINS-34128 JENKINS-43551 |
||
#22 | 21794 | Paul Allen |
Merge pull request #39 from Dohbedoh/JENKINS-34825 Jenkins 34825 |
||
#21 | 21437 | Paul Allen |
Prevent NPE on paths with @ in the parent. JENKINS-40055 |
||
#20 | 21324 | Paul Allen |
Create syncID to track syncs when polling. Original design used the client name to track the sync from previous builds, however as polling is on the master if NODE_NAME is used it breaks with slaves. JENKINS-40356 |
||
#19 | 21215 | Paul Allen |
Polling fix for Multi client support in Pipeline. Jenkins polls for each SCM checkout, so must poll each workplace and therefore need to lookup last build information for each sync. Polling now uses same lookup as Change Summary. Added extra test cases multi sync poll and poll again. JENKINS-38401 JENKINS-37462 JENKINS-39652 |
||
#18 | 21207 | Paul Allen |
Multi client support in Pipeline. If two or more `p4sync` operations are called in one Pipeline script, they MUST have different client names. During a build, multiple entries are added to the build history; on subsequent builds they are used in the three following situations: - Polling: largest change number across all the checkouts is used. - Change Summary: last change with the same client name is used. - Environment: last change recorded (regardless of client). Added two test cases to cover basic multi sync build/poll situations. JENKINS-38401 JENKINS-37462 JENKINS-39652 |
||
#17 | 20787 | Paul Allen |
Polling Fix for use with quiet period. Switched all uses of change/label to P4Revision object and implemented Comparable. The changes to build are now calculated at build time (after the quiet period) not during the polling phase. JENKINS-36883 #review-20780 |
||
#16 | 20308 | Paul Allen |
P4Groovy (experimental) Get a P4 object in groovy. Supporting basic functions: ’run’ (to run perforce commands), ‘fetch’ and ‘save’ (to access Perforce specs). |
||
#15 | 20245 | Paul Allen |
Don't disconnect after an abort. Don't disconnect from the P4 server immediately after an abort is detected. JENKINS-37487 @stuartr |
||
#14 | 20179 | Paul Allen | Javadoc fixes for java 8 builds. | ||
#13 | 19593 | Paul Allen | More minor fixes to satisfy FindBugs Analysis. | ||
#12 | 16815 | Paul Allen |
Enable early binding for CHARSET Expose CHARSET to AbstractTask to allow the charset to be set at the point the client workspace is set as current in ClientHelper. |
||
#11 | 16514 | Paul Allen |
Unshelve and resolve build step. Implements a classic Jenkins Build step (i.e. not Workflow). Must provide a shelf change number and resolve options. The shelf change number supports variable expansion ${VAR}. |
||
#10 | 15656 | Paul Allen |
Updated credentials to extend BaseStandardCredentials. Allows users to set the ID at creation. JENKINS-29702 |
||
#9 | 15430 | Paul Allen |
Trap User Abort and stop Perforce. Uses the ‘tick’ function on Progress to check if the Thread has been interrupted. If a user aborts the build then the Perforce connection is dropped at the next tick. JENKINS-26650 |
||
#8 | 15293 | Paul Allen |
Add retry attempts to Perforce Tasks. If a task fails due to an exception then the task will retry based on the value specified in the connection Credential. |
||
#7 | 13700 | Paul Allen | minor tidy up. | ||
#6 | 13681 | Paul Allen |
Abstracted Expand class from Workspace. Added support for Label variable expansion in the name and description. |
||
#5 | 13604 | Paul Allen | Improved error handling and fixed test case issue. | ||
#4 | 13603 | Paul Allen | Improved Error for Publish step when connection is down. | ||
#3 | 12976 | Paul Allen | Improved logging to include 'actual' Perforce command. | ||
#2 | 12953 | Paul Allen |
Update logging to support expand/collapse divs. - Additional Publish logging |
||
#1 | 11334 | Paul Allen |
Remote slave support for Publish Perforce Publish commands need to be executed from the remote slave. - Includes refactoring into task package |