提交 00b28d40 编写于 作者: A Andrew Mann

Merge branch 'master' into JENKINS-16942

......@@ -54,6 +54,20 @@ Upcoming changes</a>
<!-- Record your changes in the trunk here. -->
<div id="trunk" style="display:none"><!--=TRUNK-BEGIN=-->
<ul class=image>
<li class=bug>
Flyweight tasks should execute on the master if there's no static
executors available.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-7291">issue 7291</a>)
<li class=rfe>
Promote the use of 'H' in cron.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-17311">issue 17311</a>)
</ul>
</div><!--=TRUNK-END=-->
<!-- these changes are controlled by the release process. DO NOT MODIFY -->
<div id="rc" style="display:none;"><!--=BEGIN=-->
<h3><a name=v1.509>What's new in 1.509</a> <!--=DATE=--></h3>
<ul class=image>
<li class='major bug'>
Heavy thread congestion saving fingerprints.
......@@ -83,13 +97,13 @@ Upcoming changes</a>
View name should not allow "..".
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-16608">issue 16608</a>)
</ul>
</div><!--=TRUNK-END=-->
<!-- these changes are controlled by the release process. DO NOT MODIFY -->
<div id="rc" style="display:none;"><!--=BEGIN=-->
<h3><a name=v1.508>What's new in 1.508</a> <!--=DATE=--></h3>
<!--=RC-CHANGES=-->
</div><!--=END=-->
<h3><a name=v1.508>What's new in 1.508</a> (2013/03/25)</h3>
<ul class=image>
<li class='major bug'>
Fixing a regression in 1.507 that causes a failure to load matrix jobs.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-17337">issue 17337</a>)
</ul>
<h3><a name=v1.507>What's new in 1.507</a> (2013/03/24)</h3>
<ul class=image>
<li class=rfe>
......
......@@ -5,7 +5,7 @@
<parent>
<artifactId>pom</artifactId>
<groupId>org.jenkins-ci.main</groupId>
<version>1.508-SNAPSHOT</version>
<version>1.510-SNAPSHOT</version>
</parent>
<artifactId>cli</artifactId>
......
......@@ -29,7 +29,7 @@ THE SOFTWARE.
<parent>
<groupId>org.jenkins-ci.main</groupId>
<artifactId>pom</artifactId>
<version>1.508-SNAPSHOT</version>
<version>1.510-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
......
......@@ -93,7 +93,7 @@ term [int field]
returns [long bits=0]
throws ANTLRException
{
int d=1,s,e,t;
int d=NO_STEP,s,e,t;
}
: (token "-")=> s=token "-" e=token ( "/" d=token )?
{
......@@ -108,13 +108,13 @@ throws ANTLRException
{
bits = doRange(d,field);
}
| ("H" "(")=> "H" "(" s=token "-" e=token ")"
| ("H" "(")=> "H" "(" s=token "-" e=token ")" ( "/" d=token )?
{
bits = doHash(s,e);
bits = doHash(s,e,d);
}
| "H"
| "H" ( "/" d=token )?
{
bits = doHash(field);
bits = doHash(d,field);
}
;
......
......@@ -30,6 +30,7 @@ package hudson.model;
import hudson.security.AccessControlled;
import hudson.slaves.ComputerListener;
import hudson.slaves.RetentionStrategy;
import jenkins.model.Jenkins;
import org.kohsuke.stapler.StaplerFallback;
import org.kohsuke.stapler.StaplerProxy;
......@@ -116,7 +117,8 @@ public abstract class AbstractCIBase extends Node implements ItemGroup<TopLevelI
if (c!=null) {
c.setNode(n); // reuse
} else {
if(n.getNumExecutors()>0) {
// we always need Computer for the master as a fallback in case there's no other Computer.
if(n.getNumExecutors()>0 || n==Jenkins.getInstance()) {
computers.put(n, c = n.createComputer());
if (!n.isHoldOffLaunchUntilSave() && automaticSlaveLaunch) {
RetentionStrategy retentionStrategy = c.getRetentionStrategy();
......
......@@ -32,6 +32,7 @@ import hudson.cli.declarative.CLIResolver;
import hudson.console.AnnotatedLargeText;
import hudson.init.Initializer;
import hudson.model.Descriptor.FormException;
import hudson.model.Queue.FlyweightTask;
import hudson.model.labels.LabelAtom;
import hudson.model.queue.WorkUnit;
import hudson.node_monitors.NodeMonitor;
......@@ -111,7 +112,9 @@ import static javax.servlet.http.HttpServletResponse.*;
* This object is related to {@link Node} but they have some significant difference.
* {@link Computer} primarily works as a holder of {@link Executor}s, so
* if a {@link Node} is configured (probably temporarily) with 0 executors,
* you won't have a {@link Computer} object for it.
* you won't have a {@link Computer} object for it (except for the master node,
* which always get its {@link Computer} in case we have no static executors and
* we need to run a {@link FlyweightTask} - see JENKINS-7291 for more discussion.)
*
* Also, even if you remove a {@link Node}, it takes time for the corresponding
* {@link Computer} to be removed, if some builds are already in progress on that
......@@ -164,7 +167,6 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
protected final Object statusChangeLock = new Object();
public Computer(Node node) {
assert node.getNumExecutors()!=0 : "Computer created with 0 executors";
setNode(node);
}
......@@ -817,8 +819,10 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
*
* Note that if an executor dies, we'll leave it in {@link #executors} until
* the administrator yanks it out, so that we can see why it died.
*
* @since 1.509
*/
private boolean isAlive() {
protected boolean isAlive() {
for (Executor e : executors)
if (e.isAlive())
return true;
......
......@@ -1058,9 +1058,10 @@ public class Queue extends ResourceController implements Saveable {
}
});
Jenkins h = Jenkins.getInstance();
hash.add(h, h.getNumExecutors()*100);
// Even if master is configured with zero executors, we may need to run a flyweight task like MatrixProject on it.
hash.add(h, Math.max(h.getNumExecutors()*100, 1));
for (Node n : h.getNodes())
hash.add(n,n.getNumExecutors()*100);
hash.add(n, n.getNumExecutors()*100);
Label lbl = p.getAssignedLabel();
for (Node n : hash.list(p.task.getFullDisplayName())) {
......@@ -1457,7 +1458,7 @@ public class Queue extends ResourceController implements Saveable {
@Override
public String toString() {
return getClass().getName()+':'+task.toString();
return getClass().getName() + ':' + task + ':' + getWhy();
}
}
......
......@@ -87,17 +87,37 @@ abstract class BaseParser extends LLkParser {
/**
* Uses {@link Hash} to choose a random (but stable) value from within this field.
*
* @param step
* Increments. For example, 15 if "H/15". Or {@link #NO_STEP} to indicate
* the special constant for "H" without the step value.
*/
protected long doHash( int field ) {
protected long doHash(int step, int field) throws ANTLRException {
int u = UPPER_BOUNDS[field];
if (field==2) u = 28; // day of month can vary depending on month, so to make life simpler, just use [1,28] that's always safe
if (field==4) u = 6; // Both 0 and 7 of day of week are Sunday. For better distribution, limit upper bound to 6
int h = hash.next(u+1 - LOWER_BOUNDS[field]); // upper bound is inclusive
return 1L << (h+LOWER_BOUNDS[field]);
return doHash(LOWER_BOUNDS[field], u, step);
}
protected long doHash( int s, int e ) {
return 1L << (s+hash.next(e+1-s));
protected long doHash(int s, int e, int step) throws ANTLRException {
if (step > e - s + 1) {
error(Messages.BaseParser_OutOfRange(step, 1, e - s + 1));
throw new AssertionError();
} else if (step > 1) {
long bits = 0;
for (int i = hash.next(step) + s; i <= e; i += step) {
bits |= 1L << i;
}
assert bits != 0;
return bits;
} else if (step <=0) {
error(Messages.BaseParser_MustBePositive(step));
throw new AssertionError();
} else {
assert step==NO_STEP;
// step=1 (i.e. omitted) in the case of hash is actually special; means pick one value, not step by 1
return 1L << (s+hash.next(e+1-s));
}
}
protected void rangeCheck(int value, int field) throws ANTLRException {
......@@ -122,7 +142,11 @@ abstract class BaseParser extends LLkParser {
/**
* This property hashes tokens in the cron tab tokens like @daily so that they spread evenly.
* This is more aggressive optimization that changes the semantics, so not on by default.
*/
public static boolean HASH_TOKENS = Boolean.getBoolean(BaseParser.class.getName()+".hash");
public static boolean HASH_TOKENS = !"false".equals(System.getProperty(BaseParser.class.getName()+".hash"));
/**
* Constant that indicates no step value.
*/
public static final int NO_STEP = 1;
}
......@@ -29,8 +29,11 @@ import java.io.StringReader;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static java.util.Calendar.*;
import javax.annotation.CheckForNull;
/**
* Table for driving scheduled tasks.
......@@ -412,21 +415,58 @@ public final class CronTab {
* but semantically suspicious combinations, like
* "* 0 * * *"
*/
public String checkSanity() {
for( int i=0; i<5; i++ ) {
public @CheckForNull String checkSanity() {
OUTER: for (int i = 0; i < 5; i++) {
long bitMask = (i<4)?bits[i]:(long)dayOfWeek;
for( int j=BaseParser.LOWER_BOUNDS[i]; j<=BaseParser.UPPER_BOUNDS[i]; j++ ) {
if(!checkBits(bitMask,j)) {
// this rank has a sparse entry.
// if we have a sparse rank, one of them better be the left-most.
if(i>0)
return Messages.CronTab_do_you_really_mean_every_minute_when_you(spec, "0 " + spec.substring(spec.indexOf(' ')+1));
return Messages.CronTab_do_you_really_mean_every_minute_when_you(spec, "H " + spec.substring(spec.indexOf(' ') + 1));
// once we find a sparse rank, upper ranks don't matter
return null;
break OUTER;
}
}
}
String hashified = hashify(spec);
if (hashified != null) {
return Messages.CronTab_spread_load_evenly_by_using_rather_than_(hashified, spec);
}
return null;
}
/**
* Checks a prospective crontab specification to see if it could benefit from balanced hashes.
* @param spec a (legal) spec
* @return a similar spec that uses a hash, if such a transformation is necessary; null if it is OK as is
* @since 1.509
*/
public static @CheckForNull String hashify(String spec) {
if (spec.contains("H")) {
// if someone is already using H, presumably he knows what it is, so a warning is likely false positive
return null;
} else if (spec.startsWith("*/")) {// "*/15 ...." (every N minutes) to hash
return "H" + spec.substring(1);
} else if (spec.matches("\\d+ .+")) {// "0 ..." (certain minute) to hash
return "H " + spec.substring(spec.indexOf(' ') + 1);
} else {
Matcher m = Pattern.compile("0(,(\\d+)(,\\d+)*)( .+)").matcher(spec);
if (m.matches()) { // 0,15,30,45 to H/15
int period = Integer.parseInt(m.group(2));
if (period > 0) {
StringBuilder b = new StringBuilder();
for (int i = period; i < 60; i += period) {
b.append(',').append(i);
}
if (b.toString().equals(m.group(1))) {
return "H/" + period + m.group(4);
}
}
}
return null;
}
}
}
......@@ -23,6 +23,7 @@
*/
package hudson.security;
import jenkins.security.NonSerializableSecurityContext;
import jenkins.model.Jenkins;
import org.acegisecurity.AccessDeniedException;
import org.acegisecurity.Authentication;
......@@ -31,7 +32,6 @@ import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.acegisecurity.acls.sid.PrincipalSid;
import org.acegisecurity.acls.sid.Sid;
import hudson.model.Executor;
/**
* Gate-keeper that controls access to Hudson's model objects.
......@@ -116,7 +116,8 @@ public abstract class ACL {
*
* <p>
* When the impersonation is over, be sure to restore the previous authentication
* via {@code SecurityContextHolder.setContext(returnValueFromThisMethod)}.
* via {@code SecurityContextHolder.setContext(returnValueFromThisMethod)};
* or just use {@link #impersonate(Authentication,Runnable)}.
*
* <p>
* We need to create a new {@link SecurityContext} instead of {@link SecurityContext#setAuthentication(Authentication)}
......@@ -125,7 +126,23 @@ public abstract class ACL {
*/
public static SecurityContext impersonate(Authentication auth) {
SecurityContext old = SecurityContextHolder.getContext();
SecurityContextHolder.setContext(new NotSerilizableSecurityContext(auth));
SecurityContextHolder.setContext(new NonSerializableSecurityContext(auth));
return old;
}
/**
* Safer variant of {@link #impersonate(Authentication)} that does not require a finally-block.
* @param auth authentication, such as {@link #SYSTEM}
* @param body an action to run with this alternate authentication in effect
* @since 1.509
*/
public static void impersonate(Authentication auth, Runnable body) {
SecurityContext old = impersonate(auth);
try {
body.run();
} finally {
SecurityContextHolder.setContext(old);
}
}
}
......@@ -23,6 +23,7 @@
*/
package hudson.security;
import jenkins.security.NonSerializableSecurityContext;
import org.acegisecurity.context.HttpSessionContextIntegrationFilter;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.Authentication;
......@@ -43,7 +44,7 @@ import java.io.IOException;
*/
public class HttpSessionContextIntegrationFilter2 extends HttpSessionContextIntegrationFilter {
public HttpSessionContextIntegrationFilter2() throws ServletException {
setContext(NotSerilizableSecurityContext.class);
setContext(NonSerializableSecurityContext.class);
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
......
......@@ -28,6 +28,7 @@ import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.userdetails.UserDetails;
import javax.servlet.http.HttpSession;
import jenkins.security.NonSerializableSecurityContext;
/**
* {@link UserDetails} that can mark {@link Authentication} invalid.
......@@ -51,7 +52,7 @@ import javax.servlet.http.HttpSession;
* @author Kohsuke Kawaguchi
* @deprecated
* Starting 1.285, Hudson stops persisting {@link Authentication} altogether
* (see {@link NotSerilizableSecurityContext}), so there's no need to use this mechanism.
* (see {@link NonSerializableSecurityContext}), so there's no need to use this mechanism.
*/
public interface InvalidatableUserDetails extends UserDetails {
boolean isInvalid();
......
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
/*
* The MIT License
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* Copyright 2013 Jesse Glick.
*
* http://www.apache.org/licenses/LICENSE-2.0
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.security;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextImpl;
import jenkins.security.NonSerializableSecurityContext;
import org.acegisecurity.Authentication;
import org.acegisecurity.userdetails.UserDetails;
import javax.servlet.http.HttpSession;
/**
* The same as {@link SecurityContextImpl} but doesn't serialize {@link Authentication}.
*
* <p>
* {@link Authentication} often contains {@link UserDetails} implemented by a plugin,
* but when it's persisted as a part of {@link HttpSession}, such instance will never
* de-serialize correctly because the container isn't aware of additional classloading
* in Hudson.
*
* <p>
* Jenkins doesn't work with a clustering anyway, and so it's better to just not persist
* Authentication at all.
*
* See http://www.nabble.com/ActiveDirectory-Plugin%3A-ClassNotFoundException-while-loading--persisted-sessions%3A-td22085140.html
* for the problem report.
*
* @author Kohsuke Kawaguchi
* @see HttpSessionContextIntegrationFilter2
* @deprecated use {@link NonSerializableSecurityContext} instead
*/
public class NotSerilizableSecurityContext implements SecurityContext {
private transient Authentication authentication;
@Deprecated
public class NotSerilizableSecurityContext extends NonSerializableSecurityContext {
public NotSerilizableSecurityContext() {
}
public NotSerilizableSecurityContext() {}
public NotSerilizableSecurityContext(Authentication authentication) {
this.authentication = authentication;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof SecurityContext) {
SecurityContext test = (SecurityContext) obj;
if ((this.getAuthentication() == null) && (test.getAuthentication() == null)) {
return true;
}
if ((this.getAuthentication() != null) && (test.getAuthentication() != null)
&& this.getAuthentication().equals(test.getAuthentication())) {
return true;
}
}
return false;
}
public Authentication getAuthentication() {
return authentication;
}
@Override
public int hashCode() {
if (this.authentication == null) {
return -1;
} else {
return this.authentication.hashCode();
}
super(authentication);
}
public void setAuthentication(Authentication authentication) {
this.authentication = authentication;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(super.toString());
if (this.authentication == null) {
sb.append(": Null authentication");
} else {
sb.append(": Authentication: ").append(this.authentication);
}
return sb.toString();
}
}
......@@ -134,6 +134,7 @@ public class SlaveComputer extends Computer {
super(slave);
this.log = new ReopenableRotatingFileOutputStream(getLogFile(),10);
this.taskListener = new StreamTaskListener(log);
assert slave.getNumExecutors()!=0 : "Computer created with 0 executors";
}
/**
......
......@@ -66,6 +66,7 @@ import hudson.model.ManagementLink;
import hudson.model.NoFingerprintMatch;
import hudson.model.OverallLoadStatistics;
import hudson.model.Project;
import hudson.model.Queue.FlyweightTask;
import hudson.model.RestartListener;
import hudson.model.RootAction;
import hudson.model.Slave;
......@@ -3759,6 +3760,15 @@ public class Jenkins extends AbstractCIBase implements ModifiableTopLevelItemGro
return RetentionStrategy.NOOP;
}
/**
* Will always keep this guy alive so that it can function as a fallback to
* execute {@link FlyweightTask}s. See JENKINS-7291.
*/
@Override
protected boolean isAlive() {
return true;
}
/**
* Report an error.
*/
......
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jenkins.security;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextImpl;
import org.acegisecurity.Authentication;
import org.acegisecurity.userdetails.UserDetails;
import javax.servlet.http.HttpSession;
/**
* The same as {@link SecurityContextImpl} but doesn't serialize {@link Authentication}.
*
* <p>
* {@link Authentication} often contains {@link UserDetails} implemented by a plugin,
* but when it's persisted as a part of {@link HttpSession}, such instance will never
* de-serialize correctly because the container isn't aware of additional classloading
* in Hudson.
*
* <p>
* Jenkins doesn't work with a clustering anyway, and so it's better to just not persist
* Authentication at all.
*
* See <a href="http://jenkins.361315.n4.nabble.com/ActiveDirectory-Plugin-ClassNotFoundException-while-loading-persisted-sessions-tp376451.html">the problem report</a>.
*
* @author Kohsuke Kawaguchi
* @see HttpSessionContextIntegrationFilter2
* @since 1.509
*/
public class NonSerializableSecurityContext implements SecurityContext {
private transient Authentication authentication;
public NonSerializableSecurityContext() {
}
public NonSerializableSecurityContext(Authentication authentication) {
this.authentication = authentication;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof SecurityContext) {
SecurityContext test = (SecurityContext) obj;
if ((this.getAuthentication() == null) && (test.getAuthentication() == null)) {
return true;
}
if ((this.getAuthentication() != null) && (test.getAuthentication() != null)
&& this.getAuthentication().equals(test.getAuthentication())) {
return true;
}
}
return false;
}
public Authentication getAuthentication() {
return authentication;
}
@Override
public int hashCode() {
if (this.authentication == null) {
return -1;
} else {
return this.authentication.hashCode();
}
}
public void setAuthentication(Authentication authentication) {
this.authentication = authentication;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(super.toString());
if (this.authentication == null) {
sb.append(": Null authentication");
} else {
sb.append(": Authentication: ").append(this.authentication);
}
return sb.toString();
}
}
......@@ -12,4 +12,9 @@
如果您同時使用自訂工作區,所有的建置作業都會在同一個工作區中執行,除非您自己做一些特別處理,
不然建置間可能會有衝突。
在沒有自訂工作區的情況下,就算在某個節點中同時建置,Jenkins 也會使用不同的工作區避免衝突。
<p>
當 Jenkins 建立各別獨立的工作區時,會將 "@<i>編號</i>" 加到工作區目錄名稱後面,例如 "@2"。
可以在 Jenkins 指令列中指定 <code>hudson.slaves.WorkspaceList</code> 系統屬性設定分隔符號
(預設是 "@")。例如 <code>"-Dhudson.slaves.WorkspaceList=-"</code> 就是指定以破折號當成分隔符號。
</div>
......@@ -335,3 +335,5 @@ BuildAuthorizationToken.InvalidTokenProvided=\u63d0\u4f9b\u7684 Token \u7121\u65
Jenkins.CheckDisplayName.NameNotUniqueWarning=\u5df2\u7d93\u6709\u4f5c\u696d\u53eb\u505a "{0}"\uff0c\u65e5\u5f8c\u7684\u641c\u5c0b\u7d50\u679c\u53ef\u80fd\u4ee4\u4eba\u6478\u4e0d\u8457\u982d\u7dd2\u3002
Jenkins.CheckDisplayName.DisplayNameNotUniqueWarning=\u5df2\u7d93\u6709\u4f5c\u696d\u7684\u986f\u793a\u540d\u7a31\u662f "{0}"\uff0c\u53ef\u80fd\u6703\u9020\u6210\u6df7\u6dc6\u53ca\u5ef6\u9072\u3002
Jenkins.NotAllowedName="{0}" \u4e26\u4e0d\u662f\u53ef\u4ee5\u4f7f\u7528\u7684\u540d\u7a31
......@@ -24,4 +24,5 @@ BaseParser.StartEndReversed=You mean {0}-{1}?
BaseParser.MustBePositive=step must be positive, but found {0}
BaseParser.OutOfRange={0} is an invalid value. Must be within {1} and {2}
CronTab.do_you_really_mean_every_minute_when_you=Do you really mean "every minute" when you say "{0}"? Perhaps you meant "{1}"
CronTab.spread_load_evenly_by_using_rather_than_=Spread load evenly by using \u2018{0}\u2019 rather than \u2018{1}\u2019
CronTabList.InvalidInput=Invalid input: "{0}": {1}
......@@ -23,4 +23,6 @@
BaseParser.StartEndReversed=\u60a8\u662f\u6307 {0}-{1}?
BaseParser.MustBePositive=\u9593\u9694\u503c\u5fc5\u9808\u662f\u6574\u6578\uff0c\u4e0d\u904e\u60a8\u8f38\u5165 {0}
BaseParser.OutOfRange={0} \u7121\u6548\u3002\u5fc5\u9808\u4ecb\u65bc {1} \u8ddf {2} \u4e4b\u9593
CronTab.do_you_really_mean_every_minute_when_you=\
\u60a8\u78ba\u5b9a\u8981\u4f7f\u7528 "{0}" \u8a2d\u5b9a\u6210\u300c\u6bcf\u4e00\u5206\u9418\u300d? \u9084\u662f\u60a8\u5fc3\u88e1\u7684\u5176\u5be6\u662f "{1}"
CronTabList.InvalidInput=\u8f38\u5165\u7121\u6548: "{0}": {1}
......@@ -36,6 +36,13 @@ HudsonPrivateSecurityRealm.Details.PasswordError=\
HudsonPrivateSecurityRealm.ManageUserLinks.DisplayName=\u7ba1\u7406\u4f7f\u7528\u8005
HudsonPrivateSecurityRealm.ManageUserLinks.Description=\u5efa\u7acb\u3001\u522a\u9664\u6216\u662f\u4fee\u6539\u53ef\u4ee5\u767b\u5165\u5230 Jenkins \u7684\u4f7f\u7528\u8005
HudsonPrivateSecurityRealm.CreateAccount.TextNotMatchWordInImage=\u6587\u5b57\u8ddf\u5716\u7247\u88e1\u7684\u5b57\u6a23\u4e0d\u7b26
HudsonPrivateSecurityRealm.CreateAccount.PasswordNotMatch=\u5bc6\u78bc\u4e0d\u7b26
HudsonPrivateSecurityRealm.CreateAccount.PasswordRequired=\u4e00\u5b9a\u8981\u8f38\u5165\u5bc6\u78bc
HudsonPrivateSecurityRealm.CreateAccount.UserNameRequired=\u4e00\u5b9a\u8981\u8f38\u5165\u4f7f\u7528\u8005\u540d\u7a31
HudsonPrivateSecurityRealm.CreateAccount.InvalidEmailAddress=\u96fb\u5b50\u90f5\u4ef6\u4fe1\u7bb1\u7121\u6548
HudsonPrivateSecurityRealm.CreateAccount.UserNameAlreadyTaken=\u4f7f\u7528\u8005\u540d\u7a31\u5df2\u7d93\u6709\u4eba\u7528\u4e86
FullControlOnceLoggedInAuthorizationStrategy.DisplayName=\u767b\u5165\u6210\u529f\u7684\u4f7f\u7528\u8005\u53ef\u4ee5\u505a\u4efb\u4f55\u4e8b
AuthorizationStrategy.DisplayName=\u5927\u5bb6\u90fd\u53ef\u4ee5\u505a\u4efb\u4f55\u4e8b
......
......@@ -5,23 +5,23 @@
<table>
<tr>
<td>MINUTE</td>
<td>Minutes within the hour (0-59)</td>
<td>Minutes within the hour (059)</td>
</tr>
<tr>
<td>HOUR</td>
<td>The hour of the day (0-23)</td>
<td>The hour of the day (023)</td>
</tr>
<tr>
<td>DOM</td>
<td>The day of the month (1-31)</td>
<td>The day of the month (131)</td>
</tr>
<tr>
<td>MONTH</td>
<td>The month (1-12)</td>
<td>The month (112)</td>
</tr>
<tr>
<td>DOW</td>
<td>The day of the week (0-7) where 0 and 7 are Sunday.</td>
<td>The day of the week (07) where 0 and 7 are Sunday.</td>
</tr>
</table>
<p>
......@@ -29,44 +29,47 @@
available. In the order of precedence,
</p>
<ul>
<li>'*' can be used to specify all valid values.</li>
<li>'M-N' can be used to specify a range, such as "1-5"</li>
<li>'M-N/X' or '*/X' can be used to specify skips of X's value through the range,
such as "*/15" in the MINUTE field for "0,15,30,45" and "1-6/2" for "1,3,5"</li>
<li>'A,B,...,Z' can be used to specify multiple values, such as "0,30" or "1,3,5"</li>
<li><code>*</code> specifies all valid values</li>
<li><code>M-N</code> specifies a range of values</li>
<li><code>M-N/X</code> or <code>*/X</code> steps by intervals of X through the specified range or whole valid range</li>
<li><code>A,B,...,Z</code> enumerates multiple values</li>
</ul>
<p>
To allow periodically scheduled tasks to produce even load on the system,
the '<tt>H</tt>' token can be used. For example, people often use
'<tt>0 0 * * *</tt>' for a daily job, but this ends up causing a large
spike in midnight. In contrast, doing '<tt>H H * * *</tt>' would
still execute a job once a day, but the actual time of the day this gets
executed will be spread over by Jenkins.
the symbol <code>H</code> (for “hash”) should be used wherever possible.
For example, using <code>0 0 * * *</code> for a dozen daily jobs
will cause a large spike at midnight.
In contrast, using <code>H H * * *</code> would still execute each job once a day,
but not all at the same time, better using limited resources.
</p><p>
The 'H' token can be used with a range. For example, '<tt>H H(0-7) * * *</tt>'
means some time between midnight to 7:59am.
The <code>H</code> symbol can be used with a range. For example, <code>H H(0-7) * * *</code>
means some time between 12:00 AM (midnight) to 7:59 AM.
You can also use step intervals with <code>H</code>, with or without ranges.
</p><p>
The '<tt>H</tt>' token can be thought of as a random value over a range,
The <code>H</code> symbol can be thought of as a random value over a range,
but it actually is a hash of the job name, not a random function, so that
the value remains stable for any given project.
</p>
<p>
Empty lines and lines that start with '#' will be ignored as comments.
Empty lines and lines that start with <code>#</code> will be ignored as comments.
</p><p>
In addition, '@yearly', '@annually', '@monthly', '@weekly', '@daily', '@midnight',
and '@hourly' are supported.
In addition, <code>@yearly</code>, <code>@annually</code>, <code>@monthly</code>,
<code>@weekly</code>, <code>@daily</code>, <code>@midnight</code>,
and <code>@hourly</code> are supported as convenient aliases.
These use the hash system for automatic balancing.
For example, <code>@hourly</code> is the same as <code>H * * * *</code> and could mean at any time during the hour.
<code>@midnight</code> actually means some time between 12:00 AM and 2:59 AM.
</p><p>
Examples:
</p>
<table>
<tr>
<td>Examples</td>
<td>
<pre>
# every minute
* * * * *
# every 5 mins past the hour
5 * * * *
# every fifteen minutes (perhaps at :07, :22, :37, :52)
H/15 * * * *
# every ten minutes in the first half of every hour (three times, perhaps at :04, :14, :24)
H(0-29)/10 * * * *
# once every two hours every weekday (perhaps at 10:38 AM, 12:38 PM, 2:38 PM, 4:38 PM)
H 9-16/2 * * 1-5
# once a day on the 1st and 15th of every month except December
H H 1,15 1-11 *
</pre>
</td>
</tr>
</table>
</div>
\ No newline at end of file
</div>
......@@ -105,30 +105,23 @@ THE SOFTWARE.
<l:pane width="3" id="executors"
title="&lt;a href='${rootURL}/computer/'>${%Build Executor Status}&lt;/a>">
<colgroup><col width="1*"/><col width="200*"/><col width="24"/></colgroup>
<tr>
<th class="pane">#</th>
<th class="pane" colspan="2">
<div style="margin-right:19px">
${%Status}
</div>
</th>
</tr>
<j:forEach var="c" items="${computers}">
<tr>
<j:choose>
<j:when test="${c.node==app or computers.size()==1}">
<th class="pane">#</th>
<th class="pane" colspan="2">
<div style="margin-right:19px">
<j:choose>
<j:when test="${empty(app.slaves) or computers.size()==1}">
${%Status}
</j:when>
<j:otherwise>
<local:computerCaption title="${%Master}" />
</j:otherwise>
</j:choose>
</div>
</th>
</j:when>
<j:otherwise>
<th class="pane" colspan="3">
<local:computerCaption title="${c.displayName}" />
</th>
</j:otherwise>
</j:choose>
<j:if test="${computers.size() gt 1 and (c.executors.size()!=0 or c.oneOffExecutors.size()!=0)}">
<th class="pane" colspan="3">
<local:computerCaption title="${c.displayName}" />
</th>
</j:if>
</tr>
<j:forEach var="e" items="${c.executors}" varStatus="eloop">
<local:executor name="${eloop.index+1}" url="executors/${eloop.index}" />
......
......@@ -35,17 +35,12 @@ import org.jvnet.hudson.test.Bug;
import org.jvnet.hudson.test.Url;
import static java.util.Calendar.MONDAY;
import org.junit.BeforeClass;
/**
* @author Kohsuke Kawaguchi
*/
public class CronTabTest {
@BeforeClass public static void hashTokens() {
BaseParser.HASH_TOKENS = true;
}
@Test
public void test1() throws ANTLRException {
new CronTab("@yearly");
......@@ -179,14 +174,23 @@ public class CronTabTest {
}
@Test public void checkSanity() throws Exception {
assertEquals(Messages.CronTab_do_you_really_mean_every_minute_when_you("* * * * *", "0 * * * *"), new CronTab("* * * * *").checkSanity());
assertEquals(null, new CronTab("0 * * * *").checkSanity());
assertEquals(null, new CronTab("0 3 * * *").checkSanity());
assertEquals(null, new CronTab("@hourly").checkSanity());
assertEquals(Messages.CronTab_do_you_really_mean_every_minute_when_you("* * * * *", "H * * * *"), new CronTab("* * * * *").checkSanity());
assertEquals(Messages.CronTab_do_you_really_mean_every_minute_when_you("*/1 * * * *", "H * * * *"), new CronTab("*/1 * * * *").checkSanity());
assertEquals(null, new CronTab("H H(0-2) * * *", Hash.from("stuff")).checkSanity());
assertEquals(Messages.CronTab_do_you_really_mean_every_minute_when_you("* 0 * * *", "0 0 * * *"), new CronTab("* 0 * * *").checkSanity());
assertEquals(Messages.CronTab_do_you_really_mean_every_minute_when_you("* 6,18 * * *", "0 6,18 * * *"), new CronTab("* 6,18 * * *").checkSanity());
assertEquals(Messages.CronTab_do_you_really_mean_every_minute_when_you("* 0 * * *", "H 0 * * *"), new CronTab("* 0 * * *").checkSanity());
assertEquals(Messages.CronTab_do_you_really_mean_every_minute_when_you("* 6,18 * * *", "H 6,18 * * *"), new CronTab("* 6,18 * * *").checkSanity());
// dubious; could be improved:
assertEquals(Messages.CronTab_do_you_really_mean_every_minute_when_you("* * 3 * *", "0 * 3 * *"), new CronTab("* * 3 * *").checkSanity());
assertEquals(Messages.CronTab_do_you_really_mean_every_minute_when_you("* * 3 * *", "H * 3 * *"), new CronTab("* * 3 * *").checkSanity());
// promote hashes:
assertEquals(Messages.CronTab_spread_load_evenly_by_using_rather_than_("H/15 * * * *", "*/15 * * * *"), new CronTab("*/15 * * * *").checkSanity());
assertEquals(Messages.CronTab_spread_load_evenly_by_using_rather_than_("H/15 * * * *", "0,15,30,45 * * * *"), new CronTab("0,15,30,45 * * * *").checkSanity());
assertEquals(Messages.CronTab_spread_load_evenly_by_using_rather_than_("H * * * *", "0 * * * *"), new CronTab("0 * * * *").checkSanity());
assertEquals(Messages.CronTab_spread_load_evenly_by_using_rather_than_("H * * * *", "5 * * * *"), new CronTab("5 * * * *").checkSanity());
// if the user specifically asked for 3:00 AM, probably we should stick to 3:00–3:59
assertEquals(Messages.CronTab_spread_load_evenly_by_using_rather_than_("H 3 * * *", "0 3 * * *"), new CronTab("0 3 * * *").checkSanity());
assertEquals(Messages.CronTab_spread_load_evenly_by_using_rather_than_("H 22 * * 6", "00 22 * * 6"), new CronTab("00 22 * * 6").checkSanity());
assertEquals(null, new CronTab("H/15 * 1 1 *").checkSanity());
}
/**
......@@ -199,7 +203,7 @@ public class CronTabTest {
@Test
public void testHash1() throws Exception {
CronTab x = new CronTab("H H(5-8) * * *",new Hash() {
CronTab x = new CronTab("H H(5-8) H/3 H(1-10)/4 *",new Hash() {
public int next(int n) {
return n-1;
}
......@@ -207,6 +211,8 @@ public class CronTabTest {
assertEquals("59;", bitset(x.bits[0]));
assertEquals("8;", bitset(x.bits[1]));
assertEquals("3;6;9;12;15;18;21;24;27;", bitset(x.bits[2]));
assertEquals("4;8;", bitset(x.bits[3]));
}
private static String bitset(long bits) {
......@@ -221,7 +227,7 @@ public class CronTabTest {
@Test
public void testHash2() throws Exception {
CronTab x = new CronTab("H H(5-8) * * *",new Hash() {
CronTab x = new CronTab("H H(5-8) H/3 H(1-10)/4 *",new Hash() {
public int next(int n) {
return 1;
}
......@@ -229,6 +235,8 @@ public class CronTabTest {
assertEquals("1;", bitset(x.bits[0]));
assertEquals("6;", bitset(x.bits[1]));
assertEquals("2;5;8;11;14;17;20;23;26;", bitset(x.bits[2]));
assertEquals("2;6;10;", bitset(x.bits[3]));
}
@Test public void hashedMinute() throws Exception {
......@@ -240,4 +248,20 @@ public class CronTabTest {
compare(new GregorianCalendar(2013, 2, 22, 13, 56), new CronTab("H H(12-13) * * *", Hash.from("stuff")).ceil(t));
}
@Test public void hashSkips() throws Exception {
compare(new GregorianCalendar(2013, 2, 21, 16, 26), new CronTab("H/15 * * * *", Hash.from("stuff")).ceil(new GregorianCalendar(2013, 2, 21, 16, 21)));
compare(new GregorianCalendar(2013, 2, 21, 16, 41), new CronTab("H/15 * * * *", Hash.from("stuff")).ceil(new GregorianCalendar(2013, 2, 21, 16, 31)));
compare(new GregorianCalendar(2013, 2, 21, 16, 56), new CronTab("H/15 * * * *", Hash.from("stuff")).ceil(new GregorianCalendar(2013, 2, 21, 16, 42)));
compare(new GregorianCalendar(2013, 2, 21, 17, 11), new CronTab("H/15 * * * *", Hash.from("stuff")).ceil(new GregorianCalendar(2013, 2, 21, 16, 59)));
compare(new GregorianCalendar(2013, 2, 21, 0, 2), new CronTab("H(0-15)/3 * * * *", Hash.from("junk")).ceil(new GregorianCalendar(2013, 2, 21, 0, 0)));
compare(new GregorianCalendar(2013, 2, 21, 0, 2), new CronTab("H(0-3)/4 * * * *", Hash.from("junk")).ceil(new GregorianCalendar(2013, 2, 21, 0, 0)));
compare(new GregorianCalendar(2013, 2, 21, 1, 2), new CronTab("H(0-3)/4 * * * *", Hash.from("junk")).ceil(new GregorianCalendar(2013, 2, 21, 0, 5)));
try {
compare(new GregorianCalendar(2013, 2, 21, 0, 0), new CronTab("H(0-3)/15 * * * *", Hash.from("junk")).ceil(new GregorianCalendar(2013, 2, 21, 0, 0)));
fail();
} catch (ANTLRException x) {
// good
}
}
}
jenkins (1.508) unstable; urgency=low
* See http://jenkins-ci.org/changelog for more details.
-- Kohsuke Kawaguchi <kk@kohsuke.org> Mon, 25 Mar 2013 13:37:13 -0700
jenkins (1.507) unstable; urgency=low
* See http://jenkins-ci.org/changelog for more details.
......
......@@ -29,7 +29,7 @@ THE SOFTWARE.
<parent>
<groupId>org.jenkins-ci.main</groupId>
<artifactId>pom</artifactId>
<version>1.508-SNAPSHOT</version>
<version>1.510-SNAPSHOT</version>
</parent>
<artifactId>maven-plugin</artifactId>
......
......@@ -11,7 +11,7 @@
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<name>Jenkins plugin POM</name>
<version>1.508-SNAPSHOT</version>
<version>1.510-SNAPSHOT</version>
<packaging>pom</packaging>
<!--
......@@ -38,7 +38,7 @@
<dependency><!-- if a plugin wants to depend on the maven plugin, choose the right version automatically -->
<groupId>org.jenkins-ci.main</groupId>
<artifactId>maven-plugin</artifactId>
<version>1.508-SNAPSHOT</version>
<version>1.509-SNAPSHOT</version>
</dependency>
</dependencies>
</dependencyManagement>
......@@ -48,25 +48,25 @@
<groupId>org.jenkins-ci.main</groupId>
<artifactId>jenkins-war</artifactId>
<type>war</type>
<version>1.508-SNAPSHOT</version>
<version>1.509-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.main</groupId>
<artifactId>jenkins-core</artifactId>
<version>1.508-SNAPSHOT</version>
<version>1.509-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.main</groupId>
<artifactId>jenkins-test-harness</artifactId>
<version>1.508-SNAPSHOT</version>
<version>1.509-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.main</groupId>
<artifactId>ui-samples-plugin</artifactId>
<version>1.508-SNAPSHOT</version>
<version>1.509-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<!--
......
......@@ -33,7 +33,7 @@ THE SOFTWARE.
<groupId>org.jenkins-ci.main</groupId>
<artifactId>pom</artifactId>
<version>1.508-SNAPSHOT</version>
<version>1.510-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Jenkins main module</name>
......
......@@ -29,7 +29,7 @@ THE SOFTWARE.
<parent>
<artifactId>pom</artifactId>
<groupId>org.jenkins-ci.main</groupId>
<version>1.508-SNAPSHOT</version>
<version>1.510-SNAPSHOT</version>
</parent>
<groupId>org.jenkins-ci.main</groupId>
<artifactId>jenkins-test-harness</artifactId>
......
......@@ -471,4 +471,20 @@ public class MatrixProjectTest {
assertNull(defaultExecutionStrategy.getSorter());
}
@Bug(17337)
@Test public void reload() throws Exception {
MatrixProject p = j.createMatrixProject();
AxisList axes = new AxisList();
axes.add(new TextAxis("p", "only"));
p.setAxes(axes);
String n = p.getFullName();
j.buildAndAssertSuccess(p);
j.jenkins.reload();
p = j.jenkins.getItemByFullName(n, MatrixProject.class);
assertNotNull(p);
MatrixConfiguration c = p.getItem("p=only");
assertNotNull(c);
assertNotNull(c.getBuildByNumber(1));
}
}
......@@ -39,6 +39,10 @@ import hudson.triggers.TimerTrigger.TimerTriggerCause;
import hudson.util.XStream2;
import hudson.util.OneShotEvent;
import hudson.Launcher;
import hudson.matrix.LabelAxis;
import hudson.matrix.MatrixRun;
import hudson.slaves.DummyCloudImpl;
import hudson.slaves.NodeProvisioner;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
......@@ -59,8 +63,12 @@ import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* @author Kohsuke Kawaguchi
......@@ -289,6 +297,42 @@ public class QueueTest extends HudsonTestCase {
assertBuildStatusSuccess(f);
}
private int INITIALDELAY;
private int RECURRENCEPERIOD;
@Override protected void setUp() throws Exception {
INITIALDELAY = NodeProvisioner.NodeProvisionerInvoker.INITIALDELAY;
NodeProvisioner.NodeProvisionerInvoker.INITIALDELAY = 0;
RECURRENCEPERIOD = NodeProvisioner.NodeProvisionerInvoker.RECURRENCEPERIOD;
NodeProvisioner.NodeProvisionerInvoker.RECURRENCEPERIOD = 10;
super.setUp();
}
@Override protected void tearDown() throws Exception {
super.tearDown();
NodeProvisioner.NodeProvisionerInvoker.INITIALDELAY = INITIALDELAY;
NodeProvisioner.NodeProvisionerInvoker.RECURRENCEPERIOD = RECURRENCEPERIOD;
}
@Bug(7291)
public void testFlyweightTasksWithoutMasterExecutors() throws Exception {
DummyCloudImpl cloud = new DummyCloudImpl(this, 0);
cloud.label = jenkins.getLabel("remote");
jenkins.clouds.add(cloud);
jenkins.setNumExecutors(0);
jenkins.setNodes(Collections.<Node>emptyList());
MatrixProject m = createMatrixProject();
m.setAxes(new AxisList(new LabelAxis("label", Arrays.asList("remote"))));
MatrixBuild build;
try {
build = m.scheduleBuild2(0).get(60, TimeUnit.SECONDS);
} catch (TimeoutException x) {
throw (AssertionError) new AssertionError(jenkins.getQueue().getApproximateItemsQuickly().toString()).initCause(x);
}
assertBuildStatusSuccess(build);
assertEquals("", build.getBuiltOnStr());
List<MatrixRun> runs = build.getRuns();
assertEquals(1, runs.size());
assertEquals("slave0", runs.get(0).getBuiltOnStr());
}
public void testWaitForStart() throws Exception {
final OneShotEvent ev = new OneShotEvent();
FreeStyleProject p = createFreeStyleProject();
......
......@@ -29,7 +29,7 @@ THE SOFTWARE.
<parent>
<groupId>org.jenkins-ci.main</groupId>
<artifactId>pom</artifactId>
<version>1.508-SNAPSHOT</version>
<version>1.510-SNAPSHOT</version>
</parent>
<artifactId>ui-samples-plugin</artifactId>
......
......@@ -28,7 +28,7 @@ THE SOFTWARE.
<parent>
<groupId>org.jenkins-ci.main</groupId>
<artifactId>pom</artifactId>
<version>1.508-SNAPSHOT</version>
<version>1.510-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册