Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,26 @@ and as such requires several environment variables to be set:
* `AWS_REGION` should be set to, e.g., `us-east-1`.
* `CLOUDWATCH_LOG_GROUP_NAME` should be set to a test log group name, which you may need to precreate.

You may configure the above environment variables in your `~/.m2/settings.xml` and they will be applied automatically when running `mvn test`:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just making it a bit easier to run the tests, see also the changes in pom.xml.


```
<profiles>
<profile>
<id>pipeline-cloudwatch-logs</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<AWS_PROFILE>…</AWS_PROFILE>
<AWS_ROLE>…</AWS_ROLE>
<AWS_CHAINED_ROLE>…</AWS_CHAINED_ROLE>
<AWS_REGION>…</AWS_REGION>
<CLOUDWATCH_LOG_GROUP_NAME>…</CLOUDWATCH_LOG_GROUP_NAME>
</properties>
</profile>
</profiles>
```

The actual test case are defined a TCK-like
[`LogStorageTestBase`](https://github.com/jenkinsci/workflow-api-plugin/blob/907cf64feb2f38e93aebbedf87946b0235c3dc93/src/test/java/org/jenkinsci/plugins/workflow/log/LogStorageTestBase.java#L81-L295)
and you may run an individual test case if desired:
Expand Down
53 changes: 51 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
<properties>
<changelist>999999-SNAPSHOT</changelist>
<!-- https://www.jenkins.io/doc/developer/plugin-development/choosing-jenkins-baseline/ -->
<jenkins.baseline>2.492</jenkins.baseline>
<jenkins.version>${jenkins.baseline}.3</jenkins.version>
<jenkins.baseline>2.504</jenkins.baseline>
<jenkins.version>${jenkins.baseline}.1</jenkins.version>
<useBeta>true</useBeta>
<gitHubRepo>jenkinsci/${project.artifactId}-plugin</gitHubRepo>
</properties>
Expand Down Expand Up @@ -54,6 +54,22 @@
<scope>import</scope>
<type>pom</type>
</dependency>
<dependency>
<!-- TODO: https://github.com/jenkinsci/workflow-api-plugin/pull/417 -->
<!-- TODO: Remove when included in BOM -->
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-api</artifactId>
<version>1397.v02e97a_30f3ce</version>
</dependency>
<dependency>
<!-- TODO: https://github.com/jenkinsci/workflow-api-plugin/pull/417 -->
<!-- TODO: Remove when included in BOM -->
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-api</artifactId>
<version>1397.v02e97a_30f3ce</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
Expand Down Expand Up @@ -116,6 +132,12 @@
<version>2.31.33</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>ssooidc</artifactId>
<version>2.31.33</version>
<scope>test</scope>
</dependency>
Comment on lines +135 to +140
Copy link
Member Author

@dwnusbaum dwnusbaum Jul 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I needed this to do basic testing directly against the AWS credentials available in my organization. It doesn't work for testing stuff related to the agent role assumption logic (seems like it should be possible with AWS_CHAINED_ROLE, but I couldn't quite get that working after spending a few minutes with it, but will take another look at it next week), and here I am not changing any of that logic anyway.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I confirmed that this is required even after getting AWS_ROLE to work (problem was that I was trying to use the display name instead of the ARN for the role).

</dependencies>
<build>
<plugins>
Expand All @@ -140,4 +162,31 @@
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>cloudwatch-logs-settings</id>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<activation>
<property>
<name>CLOUDWATCH_LOG_GROUP_NAME</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<environmentVariables>
<AWS_PROFILE>${AWS_PROFILE}</AWS_PROFILE>
<AWS_ROLE>${AWS_ROLE}</AWS_ROLE>
<AWS_CHAINED_ROLE>${AWS_CHAINED_ROLE}</AWS_CHAINED_ROLE>
<AWS_REGION>${AWS_REGION}</AWS_REGION>
<CLOUDWATCH_LOG_GROUP_NAME>${CLOUDWATCH_LOG_GROUP_NAME}</CLOUDWATCH_LOG_GROUP_NAME>
</environmentVariables>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@
// TODO should also check DescribeLogStreams, and perhaps even CreateLogStream and PutLogEvents, to ensure roles are correct
} catch (Exception x) {
String msg = processExceptionMessage(x);
return FormValidation.error(StringUtils.abbreviate(msg, 200));
return FormValidation.error(abbreviate ? StringUtils.abbreviate(msg, 200) : msg);

Check warning on line 145 in src/main/java/io/jenkins/plugins/pipeline_cloudwatch_logs/CloudWatchAwsGlobalConfiguration.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 145 is not covered by tests
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this was just missed in #64. I needed the full message to debug some errors while I was configuring things for the tests.

}
try {
String message = LogStreamState.validate(logGroupName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@
}
if (nodeId != null && JenkinsJVM.isJenkinsJVM()) {
// Note that this does not necessarily shut down the AWSLogs client; that is shared across builds.
PipelineBridge.get().close(logStreamNameBase, buildId);
PipelineBridge.close(logStreamNameBase, buildId);

Check warning on line 154 in src/main/java/io/jenkins/plugins/pipeline_cloudwatch_logs/CloudWatchSender.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 154 is not covered by tests
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,60 +40,77 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jenkinsci.Symbol;
import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import org.jenkinsci.plugins.workflow.log.BrokenLogStorage;
import org.jenkinsci.plugins.workflow.log.LogStorage;
import org.jenkinsci.plugins.workflow.log.LogStorageFactory;
import org.jenkinsci.plugins.workflow.log.LogStorageFactoryDescriptor;
import org.kohsuke.stapler.DataBoundConstructor;

/**
* Binds CloudWatch to Pipeline logs.
*/
@Extension
public final class PipelineBridge implements LogStorageFactory {

static {
// Make sure JENKINS-52165 is enabled, or performance will be awful for remote shell steps.
System.setProperty("org.jenkinsci.plugins.workflow.steps.durable_task.DurableTaskStep.USE_WATCHING", "true");
}

private static final Logger LOGGER = Logger.getLogger(PipelineBridge.class.getName());

private final Map<String, TimestampTracker> timestampTrackers = new ConcurrentHashMap<>();
private final Map<String, LogStorageImpl> impls = new ConcurrentHashMap<>();
@DataBoundConstructor
public PipelineBridge() {}

@Override
public LogStorage forBuild(FlowExecutionOwner owner) {
final String logStreamNameBase;
final String buildId;
try {
Queue.Executable exec = owner.getExecutable();
if (exec instanceof Run) {
Run<?, ?> b = (Run<?, ?>) exec;
// TODO escape [:*@%] in job names using %XX URL encoding
logStreamNameBase = b.getParent().getFullName();
buildId = b.getId();
} else {
return null;
}
} catch (IOException x) {
return new BrokenLogStorage(x);
}
return forIDs(logStreamNameBase, buildId);
}

static PipelineBridge get() {
return ExtensionList.lookupSingleton(PipelineBridge.class);
static LogStorage forIDs(String logStreamNameBase, String buildId) {
var descriptor = ExtensionList.lookupSingleton(PipelineBridge.DescriptorImpl.class);
return descriptor.impls.computeIfAbsent(logStreamNameBase + "#" + buildId, k -> new LogStorageImpl(logStreamNameBase, buildId, descriptor.timestampTrackers));
}

LogStorage forIDs(String logStreamNameBase, String buildId) {
return impls.computeIfAbsent(logStreamNameBase + "#" + buildId, k -> new LogStorageImpl(logStreamNameBase, buildId, timestampTrackers));
static void close(String logStreamNameBase, String buildId) {
var descriptor = ExtensionList.lookupSingleton(PipelineBridge.DescriptorImpl.class);
descriptor.impls.remove(logStreamNameBase + "#" + buildId);

Check warning on line 89 in src/main/java/io/jenkins/plugins/pipeline_cloudwatch_logs/PipelineBridge.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 60-89 are not covered by tests
}

void close(String logStreamNameBase, String buildId) {
impls.remove(logStreamNameBase + "#" + buildId);
@Extension
@Symbol("amazonCloudWatchLogs")
public static class DescriptorImpl extends LogStorageFactoryDescriptor<PipelineBridge> {
static {
// Make sure JENKINS-52165 is enabled, or performance will be awful for remote shell steps.
System.setProperty("org.jenkinsci.plugins.workflow.steps.durable_task.DurableTaskStep.USE_WATCHING", "true");
}

private final Map<String, TimestampTracker> timestampTrackers = new ConcurrentHashMap<>();
private final Map<String, LogStorageImpl> impls = new ConcurrentHashMap<>();

@Override
public String getDisplayName() {
return "Amazon CloudWatch Logs";
}

@Override
public PipelineBridge getDefaultInstance() {
return new PipelineBridge();

Check warning on line 110 in src/main/java/io/jenkins/plugins/pipeline_cloudwatch_logs/PipelineBridge.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 110 is not covered by tests
}
}

private static class LogStorageImpl implements LogStorage {

private final String logStreamNameBase;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ static void globalConfiguration() throws Exception {
}

@Override protected LogStorage createStorage() {
return PipelineBridge.get().forIDs(LOG_STREAM_NAME, id);
return PipelineBridge.forIDs(LOG_STREAM_NAME, id);
}

@Override protected Map<String, Level> agentLoggers() {
Expand Down