Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[JEP-210] Log handling rewrite #17

Merged
merged 43 commits into from
Oct 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
ca68b43
[JENKINS-38381] Prototype of log handling rewrite.
jglick Sep 23, 2016
7c2b090
Pick up https://github.com/jenkinsci/maven-hpi-plugin/pull/25.
jglick Sep 26, 2016
58c3424
Merge branch 'master' into logs-JENKINS-38381
jglick Sep 26, 2016
bfa0a61
Clarification of getListener Javadoc.
jglick Sep 30, 2016
6846366
2.16 parent
jglick Oct 3, 2016
dee29e7
Merge branch 'master' into logs-JENKINS-38381
jglick Dec 7, 2016
99f1c51
Updated parent to pick up jitpack.io support.
jglick Dec 7, 2016
ea218b9
getLog should take a long start argument.
jglick Dec 21, 2016
2a7494c
Merge branch 'master' into logs-JENKINS-38381
jglick Jan 3, 2017
c2dd464
Merge branch 'master' into logs-JENKINS-38381
jglick Feb 2, 2018
834dce0
Merge branch 'incrementals' into logs-JENKINS-38381
jglick Jun 11, 2018
c2c17ba
Merge branch 'master' into logs-JENKINS-38381
jglick Jun 15, 2018
2f7745d
Merge branch 'master' into logs-JENKINS-38381
jglick Jun 15, 2018
69b13f7
Marking getLog as beta, and at least for now making it fail if not ov…
jglick Jun 18, 2018
e1e30db
Added boolean complete flag to FlowExecutionOwner.getLog.
jglick Jun 18, 2018
58f0128
Refactored API to center around more abstract LogStorage interface.
jglick Jun 20, 2018
239019e
Javadoc error.
jglick Jun 20, 2018
0cdfdc7
Override DummyOwner.getListener.
jglick Jul 31, 2018
b1d5771
Merge branch 'master' into logs-JENKINS-38381
jglick Aug 6, 2018
5b65e4f
Updates.
jglick Aug 6, 2018
0fa5a94
Javadoc links to JEP-210.
jglick Aug 8, 2018
1b90748
StreamLogStorage did not need a FlowExecutionOwner.
jglick Aug 8, 2018
f338284
Introduced LogStorageTestBase to better verify detailed behavior of p…
jglick Aug 9, 2018
8e64fdc
Also asserting AnnotatedLargeText.length values.
jglick Aug 9, 2018
95b33eb
Generalized StreamLogStorageTest.mangledLines to a blackbox test.
jglick Aug 9, 2018
ca5975e
Also verifying that console annotations and node spans properly nest.
jglick Aug 9, 2018
1d2511e
Replacing StreamLogStorage with a more efficient FileLogStorage.
jglick Aug 13, 2018
db5a05a
Removing StreamLogStorage as it is now unused.
jglick Aug 15, 2018
1d32618
Comment.
jglick Aug 15, 2018
9a017bd
Forgotten @Override.
jglick Aug 15, 2018
d4e41df
LogStorageTestBase was not correctly handling [Annotated]LargeText.is…
jglick Aug 16, 2018
aba6b09
Added LogStorageTestBase.remoting.
jglick Aug 17, 2018
48e78d3
Windows test fix.
jglick Aug 17, 2018
e30e923
Tweaks to FileLogStorage suggested by @dwnusbaum.
jglick Aug 17, 2018
e25e1ec
FindBugs
jglick Aug 17, 2018
c9c8e89
Documenting and at least smoke-testing AutoCloseable on step logs.
jglick Aug 23, 2018
79e3cc4
FileLogStorage was calling Reader.read() past EOF, which NullReader c…
jglick Sep 7, 2018
d3c3b14
CI outage, so may as well bump up parent!
jglick Sep 7, 2018
ba604ce
Windows test failure due to a lock on log-index.
jglick Sep 8, 2018
be9be0c
Review comments from @svanoort.
jglick Sep 28, 2018
0293799
Defend against hypothetical negative array sizes.
jglick Sep 28, 2018
a63fe88
Merge branch 'master' into logs-JENKINS-38381
jglick Sep 28, 2018
999cef3
Tweaking handling of corrupt content in index-log.
jglick Oct 1, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .mvn/extensions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
<extension>
<groupId>io.jenkins.tools.incrementals</groupId>
<artifactId>git-changelist-maven-extension</artifactId>
<version>1.0-beta-3</version>
<version>1.0-beta-4</version>
</extension>
</extensions>
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>3.14</version>
<version>3.21</version>
<relativePath />
</parent>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import jenkins.model.TransientActionFactory;
import org.jenkinsci.plugins.workflow.log.LogStorage;
import org.jenkinsci.plugins.workflow.steps.StepContext;

/**
Expand Down Expand Up @@ -121,9 +122,15 @@ public String getUrlOfExecution() throws IOException {
/**
* Gets a listener to which we may print general messages.
* Normally {@link StepContext#get} should be used, but in some cases there is no associated step.
* <p>The listener should be remotable: if sent to an agent, messages printed to it should still appear in the log.
* The same will then apply to calls to {@link StepContext#get} on {@link TaskListener}.
*/
public @Nonnull TaskListener getListener() throws IOException {
return TaskListener.NULL;
try {
return LogStorage.of(this).overallListener();
} catch (InterruptedException x) {
throw new IOException(x);
}
}

/**
Expand Down Expand Up @@ -167,6 +174,9 @@ private static class DummyOwner extends FlowExecutionOwner {
@Override public int hashCode() {
return 0;
}
@Override public TaskListener getListener() throws IOException {
return TaskListener.NULL;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* The MIT License
*
* Copyright 2018 CloudBees, Inc.
*
* 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:
*
* 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 org.jenkinsci.plugins.workflow.log;

import hudson.Functions;
import hudson.console.AnnotatedLargeText;
import hudson.model.BuildListener;
import hudson.model.TaskListener;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.Beta;
import org.kohsuke.stapler.framework.io.ByteBuffer;

/**
* Placeholder for storage broken by some kind of access error.
*/
@Restricted(Beta.class)
public final class BrokenLogStorage implements LogStorage {

private final Throwable x;

public BrokenLogStorage(Throwable x) {
this.x = x;
}

@Override public BuildListener overallListener() throws IOException, InterruptedException {
throw new IOException(x);
}

@Override public TaskListener nodeListener(FlowNode node) throws IOException, InterruptedException {
throw new IOException(x);
}

@Override public AnnotatedLargeText<FlowExecutionOwner.Executable> overallLog(FlowExecutionOwner.Executable build, boolean complete) {
return new BrokenAnnotatedLargeText<>();
}

@Override public AnnotatedLargeText<FlowNode> stepLog(FlowNode node, boolean complete) {
return new BrokenAnnotatedLargeText<>();
}

private class BrokenAnnotatedLargeText<T> extends AnnotatedLargeText<T> {

BrokenAnnotatedLargeText() {
super(makeByteBuffer(), StandardCharsets.UTF_8, true, null);
}

}

private ByteBuffer makeByteBuffer() {
ByteBuffer buf = new ByteBuffer();
byte[] stack = Functions.printThrowable(x).getBytes(StandardCharsets.UTF_8);
try {
buf.write(stack, 0, stack.length);
} catch (IOException x2) {
assert false : x2;
}
return buf;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* The MIT License
*
* Copyright 2018 CloudBees, Inc.
*
* 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:
*
* 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 org.jenkinsci.plugins.workflow.log;

import com.jcraft.jzlib.GZIPInputStream;
import com.jcraft.jzlib.GZIPOutputStream;
import com.trilead.ssh2.crypto.Base64;
import hudson.console.AnnotatedLargeText;
import hudson.console.ConsoleAnnotationOutputStream;
import hudson.console.ConsoleAnnotator;
import hudson.remoting.ClassFilter;
import hudson.remoting.ObjectInputStreamEx;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import static java.lang.Math.abs;
import java.util.concurrent.TimeUnit;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import jenkins.model.Jenkins;
import jenkins.security.CryptoConfidentialKey;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.Beta;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;

/**
* Some utility code extracted from {@link AnnotatedLargeText} which probably belongs in {@link ConsoleAnnotator} or {@link ConsoleAnnotationOutputStream}.
*/
@Restricted(Beta.class)
public class ConsoleAnnotators {

private static final CryptoConfidentialKey PASSING_ANNOTATOR = new CryptoConfidentialKey(ConsoleAnnotators.class, "consoleAnnotator");

/**
* What to pass to {@link ConsoleAnnotationOutputStream#ConsoleAnnotationOutputStream} when overriding {@link AnnotatedLargeText#writeHtmlTo}.
*/
public static <T> ConsoleAnnotator<T> createAnnotator(T context) throws IOException {
StaplerRequest req = Stapler.getCurrentRequest();
try {
String base64 = req != null ? req.getHeader("X-ConsoleAnnotator") : null;
if (base64 != null) {
@SuppressWarnings("deprecation") // TODO still used in the AnnotatedLargeText version
Cipher sym = PASSING_ANNOTATOR.decrypt();
try (ObjectInputStream ois = new ObjectInputStreamEx(new GZIPInputStream(
new CipherInputStream(new ByteArrayInputStream(Base64.decode(base64.toCharArray())), sym)),
Jenkins.get().pluginManager.uberClassLoader,
ClassFilter.DEFAULT)) {
long timestamp = ois.readLong();
if (TimeUnit.HOURS.toMillis(1) > abs(System.currentTimeMillis() - timestamp)) {
@SuppressWarnings("unchecked") ConsoleAnnotator<T> annotator = (ConsoleAnnotator) ois.readObject();
return annotator;
}
}
}
} catch (ClassNotFoundException e) {
throw new IOException(e);
}
return ConsoleAnnotator.initial(context);
}

/**
* What to call at the end of an override of {@link AnnotatedLargeText#writeHtmlTo}.
*/
public static void setAnnotator(ConsoleAnnotator<?> annotator) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
@SuppressWarnings("deprecation") // TODO still used in the AnnotatedLargeText version
Cipher sym = PASSING_ANNOTATOR.encrypt();
try (ObjectOutputStream oos = new ObjectOutputStream(new GZIPOutputStream(new CipherOutputStream(baos, sym)))) {
oos.writeLong(System.currentTimeMillis());
oos.writeObject(annotator);
}
StaplerResponse rsp = Stapler.getCurrentResponse();
if (rsp != null) {
rsp.setHeader("X-ConsoleAnnotator", new String(Base64.encode(baos.toByteArray())));
}
}

private ConsoleAnnotators() {}

}
Loading