package org.jenkinsci.plugins.p4.tasks; import hudson.AbortException; import hudson.EnvVars; import hudson.FilePath; import hudson.model.Item; import hudson.model.Run; import hudson.model.TaskListener; import hudson.remoting.Channel; import hudson.remoting.VirtualChannel; 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.workspace.StaticWorkspaceImpl; 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.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 syncID; 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; } @Deprecated public void setCredential(String credential) { this.credential = ConnectionHelper.findCredential(credential); } public void setCredential(String credential, Item project) { this.credential = ConnectionHelper.findCredential(credential, project); } public void setCredential(String credential, Run run) { this.credential = ConnectionHelper.findCredential(credential, run); } 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.syncID = workspace.getSyncID(); 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); String node = getNodeName(buildWorkspace); envVars.put("NODE_NAME", envVars.get("NODE_NAME", node)); ws.setExpand(envVars); // Set workspace root (check for parallel execution) String root = buildWorkspace.getRemote(); if (root.contains("@")) { root = root.replace("@", "%40"); } // Template workspace for parallel execution String name = buildWorkspace.getName(); if (name.matches(".*@[0-9]+")) { ws = cloneWorkspace(ws, name, envVars); } ws.setRootPath(root); if (ws.isPinHost()) { String hostname = getHostName(buildWorkspace); ws.setHostName(hostname); } else { ws.setHostName(""); } return ws; } private Workspace cloneWorkspace(Workspace ws, String name, EnvVars envVars) throws AbortException { // Skip cloning the workspace if clone option is not set if(ws instanceof StaticWorkspaceImpl) { StaticWorkspaceImpl staticWs = (StaticWorkspaceImpl)ws; if(!staticWs.isClone()) { return ws; } } String[] parts = name.split("@"); if (parts.length == 2) { String exec = parts[1]; // Update Workspace before cloning setWorkspace(ws); // Template workspace to .cloneN (where N is the @ number) try { int n = Integer.parseInt(exec); String charset = ws.getCharset(); boolean pin = ws.isPinHost(); String fullName = ws.getFullName(); String template = fullName + ".clone" + n; ws = new TemplateWorkspaceImpl(charset, pin, fullName, template); ws.setExpand(envVars); } catch (NumberFormatException e) { // do not template; e.g. 'script' keeps original name } } return ws; } private String getNodeName(FilePath build) { VirtualChannel vc = build.getChannel(); if (vc instanceof Channel) { Channel channel = (Channel) vc; return channel.getName(); } return "master"; } /** * 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 ""; } } public String getClient() { return client; } public String getSyncID() { return syncID; } 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) { p4.disconnect(); 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 | |
---|---|---|---|---|---|
#3 | 22326 | Paul Allen | Merging down using p4-jenkins | ||
#2 | 22046 | Paul Allen | Merge down 1.6.1 release changes from Main. | ||
#1 | 21940 | Paul Allen | Branching using p4-jenkins | ||
//guest/perforce_software/p4jenkins/main/src/main/java/org/jenkinsci/plugins/p4/tasks/AbstractTask.java | |||||
#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 |