├── .travis.yml
├── src
├── test
│ ├── resources
│ │ ├── mockito-extensions
│ │ │ └── org.mockito.plugins.MockMaker
│ │ └── jenkinsci
│ │ │ └── plugins
│ │ │ └── influxdb
│ │ │ ├── generators
│ │ │ └── sonarqube
│ │ │ │ ├── report-task.txt
│ │ │ │ └── build-log.txt
│ │ │ ├── ConfigurationAsCodeTest
│ │ │ └── configuration-as-code.yml
│ │ │ └── IntegrationBaseTest
│ │ │ └── configuration-as-code.yml
│ └── java
│ │ └── jenkinsci
│ │ └── plugins
│ │ └── influxdb
│ │ ├── generators
│ │ ├── serenity
│ │ │ ├── SerenityCannedJsonSummaryFile.java
│ │ │ ├── SerenityJsonSummaryFileTest.java
│ │ │ └── SerenityPointGeneratorTest.java
│ │ ├── MetricsPointGeneratorTest.java
│ │ ├── CustomDataPointGeneratorTest.java
│ │ ├── GitPointGeneratorTest.java
│ │ ├── CustomDataMapPointGeneratorTest.java
│ │ ├── PerfPublisherPointGeneratorTest.java
│ │ ├── AgentPointGeneratorTest.java
│ │ ├── PointGeneratorBaseTest.java
│ │ ├── JUnitPointGeneratorTest.java
│ │ └── ChangeLogGeneratorTest.java
│ │ ├── renderer
│ │ └── ProjectNameRendererTest.java
│ │ ├── InfluxDbPublisherTest.java
│ │ ├── ConfigurationAsCodeTest.java
│ │ └── IntegrationTest.java
└── main
│ ├── resources
│ ├── jenkinsci
│ │ └── plugins
│ │ │ └── influxdb
│ │ │ ├── models
│ │ │ └── Target
│ │ │ │ ├── help-url.html
│ │ │ │ ├── help-database.html
│ │ │ │ ├── help-globalListenerFilter.html
│ │ │ │ ├── help-usingJenkinsProxy.html
│ │ │ │ ├── help-description.html
│ │ │ │ ├── help-globalListener.html
│ │ │ │ ├── help-organization.html
│ │ │ │ ├── help-jobScheduledTimeAsPointsTimestamp.html
│ │ │ │ ├── help-exposeExceptions.html
│ │ │ │ ├── help-retentionPolicy.html
│ │ │ │ └── config.jelly
│ │ │ ├── InfluxDbStep
│ │ │ ├── help-selectedTarget.html
│ │ │ ├── help-customPrefix.html
│ │ │ ├── help-customProjectName.html
│ │ │ ├── help-jenkinsEnvParameterTag.html
│ │ │ ├── help-jenkinsEnvParameterField.html
│ │ │ └── config.jelly
│ │ │ ├── InfluxDbPublisher
│ │ │ ├── help-selectedTarget.html
│ │ │ ├── help-customPrefix.html
│ │ │ ├── help-customProjectName.html
│ │ │ ├── help-jenkinsEnvParameterTag.html
│ │ │ ├── help-jenkinsEnvParameterField.html
│ │ │ └── config.jelly
│ │ │ └── InfluxDbGlobalConfig
│ │ │ └── config.jelly
│ └── index.jelly
│ ├── java
│ └── jenkinsci
│ │ └── plugins
│ │ └── influxdb
│ │ ├── renderer
│ │ ├── MeasurementRenderer.java
│ │ └── ProjectNameRenderer.java
│ │ ├── generators
│ │ ├── serenity
│ │ │ ├── ISerenityJsonSummaryFile.java
│ │ │ ├── SerenityJsonSummaryFile.java
│ │ │ └── SerenityPointGenerator.java
│ │ ├── TimeGenerator.java
│ │ ├── PointGenerator.java
│ │ ├── CustomDataMapPointGenerator.java
│ │ ├── CustomDataPointGenerator.java
│ │ ├── JacocoPointGenerator.java
│ │ ├── MetricsPointGenerator.java
│ │ ├── GitPointGenerator.java
│ │ ├── CoveragePointGenerator.java
│ │ ├── CoberturaPointGenerator.java
│ │ ├── PerformancePointGenerator.java
│ │ ├── JUnitPointGenerator.java
│ │ ├── ChangeLogPointGenerator.java
│ │ ├── AbstractPointGenerator.java
│ │ ├── AgentPointGenerator.java
│ │ ├── PerfPublisherPointGenerator.java
│ │ └── RobotFrameworkPointGenerator.java
│ │ ├── InfluxReportException.java
│ │ ├── InfluxDbStepExecution.java
│ │ ├── InfluxDbGlobalConfig.java
│ │ ├── models
│ │ └── AbstractPoint.java
│ │ ├── global
│ │ └── GlobalRunListener.java
│ │ ├── InfluxDbStep.java
│ │ └── InfluxDbPublisher.java
│ └── main.iml
├── doc
├── img
│ ├── advanced-options.png
│ ├── post-build-action.png
│ ├── jenkins-configuration.png
│ └── select-influxdb-target.png
├── breaking_changes.md
└── SonarQube_integration.md
├── .gitignore
├── .github
├── release-drafter.yml
└── workflows
│ ├── release-drafter.yml
│ ├── jenkins-security-scan.yml
│ └── codeql-analysis.yml
├── Jenkinsfile
└── LICENSE
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 |
--------------------------------------------------------------------------------
/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker:
--------------------------------------------------------------------------------
1 | mock-maker-inline
2 |
--------------------------------------------------------------------------------
/src/main/resources/jenkinsci/plugins/influxdb/models/Target/help-url.html:
--------------------------------------------------------------------------------
1 | URL of the InfluxDB.
2 |
--------------------------------------------------------------------------------
/src/main/resources/jenkinsci/plugins/influxdb/models/Target/help-database.html:
--------------------------------------------------------------------------------
1 | Name of the database.
2 |
--------------------------------------------------------------------------------
/doc/img/advanced-options.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/influxdb-plugin/HEAD/doc/img/advanced-options.png
--------------------------------------------------------------------------------
/doc/img/post-build-action.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/influxdb-plugin/HEAD/doc/img/post-build-action.png
--------------------------------------------------------------------------------
/doc/img/jenkins-configuration.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/influxdb-plugin/HEAD/doc/img/jenkins-configuration.png
--------------------------------------------------------------------------------
/doc/img/select-influxdb-target.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/influxdb-plugin/HEAD/doc/img/select-influxdb-target.png
--------------------------------------------------------------------------------
/src/main/resources/jenkinsci/plugins/influxdb/models/Target/help-globalListenerFilter.html:
--------------------------------------------------------------------------------
1 | Regular expression to match only certain jobs.
2 |
--------------------------------------------------------------------------------
/src/main/resources/jenkinsci/plugins/influxdb/models/Target/help-usingJenkinsProxy.html:
--------------------------------------------------------------------------------
1 | Use the global Jenkins proxy configuration when connecting to this server.
2 |
--------------------------------------------------------------------------------
/src/main/resources/jenkinsci/plugins/influxdb/InfluxDbStep/help-selectedTarget.html:
--------------------------------------------------------------------------------
1 | InfluxDB target to send data to. Targets have to be configured in the global Jenkins settings.
2 |
--------------------------------------------------------------------------------
/src/main/resources/jenkinsci/plugins/influxdb/InfluxDbPublisher/help-selectedTarget.html:
--------------------------------------------------------------------------------
1 | InfluxDB target to send data to. Targets have to be configured in the global Jenkins settings.
2 |
--------------------------------------------------------------------------------
/src/main/resources/jenkinsci/plugins/influxdb/models/Target/help-description.html:
--------------------------------------------------------------------------------
1 | Description for the InfluxDB target.
2 |
This is used in a job's configuration to select which InfluxDB target to use.
3 |
--------------------------------------------------------------------------------
/src/main/java/jenkinsci/plugins/influxdb/renderer/MeasurementRenderer.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.renderer;
2 |
3 | public interface MeasurementRenderer {
4 |
5 | String render(T input);
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/resources/index.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 | This plugin allows sending build results to InfluxDB. It was inspired by https://github.com/jrajala-eficode/jenkins-ci.influxdb-plugin
4 |
5 |
--------------------------------------------------------------------------------
/src/main/resources/jenkinsci/plugins/influxdb/InfluxDbStep/help-customPrefix.html:
--------------------------------------------------------------------------------
1 | A custom prefix that gets prepended to the job name, for example to prevent several 'master' metrics for different jobs in multi branch pipeline jobs.
2 |
--------------------------------------------------------------------------------
/src/main/resources/jenkinsci/plugins/influxdb/InfluxDbPublisher/help-customPrefix.html:
--------------------------------------------------------------------------------
1 | A custom prefix that gets prepended to the job name, for example to prevent several 'master' metrics for different jobs in multi branch pipeline jobs.
2 |
--------------------------------------------------------------------------------
/src/main/resources/jenkinsci/plugins/influxdb/models/Target/help-globalListener.html:
--------------------------------------------------------------------------------
1 | Whether to globally enable publishing build results to this target.
2 | Even if enabled, it is still possible to configure an additional target per job.
3 |
--------------------------------------------------------------------------------
/src/main/resources/jenkinsci/plugins/influxdb/models/Target/help-organization.html:
--------------------------------------------------------------------------------
1 | Leave this empty when using InfluxDB 1.x targets!
2 |
3 |
4 | Organization name for InfluxDB 2.x targets. If this field is blank, the plugin interprets it as a InfluxDB 1.x target.
--------------------------------------------------------------------------------
/src/main/resources/jenkinsci/plugins/influxdb/InfluxDbStep/help-customProjectName.html:
--------------------------------------------------------------------------------
1 | Sets a custom value for the InfluxDB 'project_name' tag key, that overrides the default, which is the job name.
2 | Useful to easily group metrics for different jobs in multi branch pipeline jobs.
3 |
--------------------------------------------------------------------------------
/src/main/resources/jenkinsci/plugins/influxdb/InfluxDbPublisher/help-customProjectName.html:
--------------------------------------------------------------------------------
1 | Sets a custom value for the InfluxDB 'project_name' tag key, that overrides the default, which is the job name.
2 | Useful to easily group metrics for different jobs in multi branch pipeline jobs.
3 |
--------------------------------------------------------------------------------
/src/main/resources/jenkinsci/plugins/influxdb/models/Target/help-jobScheduledTimeAsPointsTimestamp.html:
--------------------------------------------------------------------------------
1 | Which timestamp to use for InfluxDB points.
2 |
3 | If enabled, the time when a job is scheduled.
4 | If disabled, the time when a point is created (the job is almost done).
5 |
6 |
--------------------------------------------------------------------------------
/src/test/resources/jenkinsci/plugins/influxdb/generators/sonarqube/report-task.txt:
--------------------------------------------------------------------------------
1 | projectKey=InfluxDBPlugin
2 | serverUrl=http://sonarqube:9000
3 | serverVersion=9.9.1.69595
4 | dashboardUrl=http://sonarqube:9000/dashboard?id=InfluxDBPlugin
5 | ceTaskId=123EXAMPLE
6 | ceTaskUrl=http://sonarqube:9000/api/ce/task?id=123EXAMPLE
--------------------------------------------------------------------------------
/src/main/resources/jenkinsci/plugins/influxdb/models/Target/help-exposeExceptions.html:
--------------------------------------------------------------------------------
1 | If enabled, exceptions communicating with InfluxDB are exposed and jobs may fail if a report could not be written to InfluxDB.
2 | If disabled, even if an exception occurs while connecting to InfluxDB, this exception is not exposed but just logged.
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !src/main/resources/jenkinsci/plugins/influxdb/models/Target
3 |
4 | *.swp
5 | pom.xml.releaseBackup
6 | work/
7 | release.properties
8 | .idea
9 | *.iml
10 | .DS_Store
11 | changes.txt
12 |
13 | # Local VS code created hidden directories
14 | .classpath
15 | .factorypath
16 | .project
17 | .settings/
18 | .vscode/
19 |
--------------------------------------------------------------------------------
/src/main/java/jenkinsci/plugins/influxdb/generators/serenity/ISerenityJsonSummaryFile.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.generators.serenity;
2 |
3 | import java.io.IOException;
4 | import java.nio.file.Path;
5 |
6 | public interface ISerenityJsonSummaryFile {
7 | boolean exists();
8 | Path getPath();
9 | String getContents() throws IOException;
10 | }
--------------------------------------------------------------------------------
/src/main/resources/jenkinsci/plugins/influxdb/InfluxDbStep/help-jenkinsEnvParameterTag.html:
--------------------------------------------------------------------------------
1 | Custom tag set that will be added to all measurements, configured as key-value pairs (one per line, in Java Properties file format).
2 | Current build parameters and/or environment variables can be used in the form of ${PARAM}
3 |
4 | - KEY=${PARAM}
5 | - KEY=PREFIX_${PARAM}_SUFFIX
6 | - KEY=VALUE
7 |
8 |
--------------------------------------------------------------------------------
/src/main/resources/jenkinsci/plugins/influxdb/InfluxDbPublisher/help-jenkinsEnvParameterTag.html:
--------------------------------------------------------------------------------
1 | Custom tag set that will be added to all measurements, configured as key-value pairs (one per line, in Java Properties file format).
2 | Current build parameters and/or environment variables can be used in the form of ${PARAM}
3 |
4 | - KEY=${PARAM}
5 | - KEY=PREFIX_${PARAM}_SUFFIX
6 | - KEY=VALUE
7 |
8 |
--------------------------------------------------------------------------------
/src/main/resources/jenkinsci/plugins/influxdb/InfluxDbStep/help-jenkinsEnvParameterField.html:
--------------------------------------------------------------------------------
1 | Custom field set that will be added to the default measurement 'jenkins_data', configured as key-value pairs (one per line, in Java Properties file format).
2 | Current build parameters and/or environment variables can be used in the form of ${PARAM}
3 |
4 | - KEY=${PARAM}
5 | - KEY=PREFIX_${PARAM}_SUFFIX
6 | - KEY=VALUE
7 |
8 |
--------------------------------------------------------------------------------
/src/main/java/jenkinsci/plugins/influxdb/generators/TimeGenerator.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.generators;
2 |
3 | public class TimeGenerator {
4 |
5 | private final long currentTime;
6 | private long nanoOffSet = 0;
7 |
8 | TimeGenerator(long currentTime) {
9 | this.currentTime = currentTime;
10 | }
11 |
12 | public long next() {
13 | nanoOffSet++;
14 | return currentTime + nanoOffSet;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/resources/jenkinsci/plugins/influxdb/InfluxDbPublisher/help-jenkinsEnvParameterField.html:
--------------------------------------------------------------------------------
1 | Custom field set that will be added to the default measurement 'jenkins_data', configured as key-value pairs (one per line, in Java Properties file format).
2 | Current build parameters and/or environment variables can be used in the form of ${PARAM}
3 |
4 | - KEY=${PARAM}
5 | - KEY=PREFIX_${PARAM}_SUFFIX
6 | - KEY=VALUE
7 |
8 |
--------------------------------------------------------------------------------
/src/main/main.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/main/resources/jenkinsci/plugins/influxdb/models/Target/help-retentionPolicy.html:
--------------------------------------------------------------------------------
1 | The retention policy for the data (i.e. how long the data is stored).
2 |
3 | The default value for InfluxDB 1.0+ versions is "autogen".
4 | The default value for InfluxDB <1.0 versions is "default".
5 |
6 | The "autogen" or "default" retention policies mean the data is stored for infinity.
7 | The input can be set e.g. "2m", "3d", "4w", for 2 minutes, 3 days, or 4 weeks respectively.
8 |
--------------------------------------------------------------------------------
/.github/release-drafter.yml:
--------------------------------------------------------------------------------
1 | _extends: .github
2 | name-template: $NEXT_PATCH_VERSION
3 | tag-template: influxdb-$NEXT_VERSION_VERSION
4 | version-template: $MAJOR.$MINOR.$PATCH
5 | categories:
6 | - title: '🚀 Features'
7 | labels:
8 | - 'feature'
9 | - 'enhancement'
10 | - title: '🐛 Bug Fixes'
11 | labels:
12 | - 'fix'
13 | - 'bugfix'
14 | - 'bug'
15 | - title: '🧰 Maintenance'
16 | label: 'maintenance'
17 | exclude-labels:
18 | - 'skip-changelog'
19 |
--------------------------------------------------------------------------------
/Jenkinsfile:
--------------------------------------------------------------------------------
1 | /*
2 | See the documentation for more options:
3 | https://github.com/jenkins-infra/pipeline-library/
4 | */
5 | buildPlugin(
6 | forkCount: '1C', // run this number of tests in parallel for faster feedback. If the number terminates with a 'C', the value will be multiplied by the number of available CPU cores
7 | useContainerAgent: false, // Set to `false` if you need to use Docker for containerized tests
8 | configurations: [
9 | [platform: 'linux', jdk: 21],
10 | [platform: 'windows', jdk: 17],
11 | ])
--------------------------------------------------------------------------------
/.github/workflows/release-drafter.yml:
--------------------------------------------------------------------------------
1 | # Note: additional setup is required, see https://github.com/jenkinsci/.github/blob/master/.github/release-drafter.adoc
2 |
3 | name: Release Drafter
4 |
5 | on:
6 | push:
7 | branches:
8 | - development
9 |
10 | jobs:
11 | update_release_draft:
12 | runs-on: ubuntu-latest
13 | steps:
14 | # Drafts your next Release notes as Pull Requests are merged into the default branch
15 | - uses: release-drafter/release-drafter@v5
16 | env:
17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
18 |
--------------------------------------------------------------------------------
/src/test/resources/jenkinsci/plugins/influxdb/ConfigurationAsCodeTest/configuration-as-code.yml:
--------------------------------------------------------------------------------
1 | unclassified:
2 | influxDbGlobalConfig:
3 | targets:
4 | - credentialsId: "some_id"
5 | database: "some_database"
6 | description: "some description"
7 | exposeExceptions: true
8 | globalListener: true
9 | globalListenerFilter: "some filter"
10 | jobScheduledTimeAsPointsTimestamp: true
11 | organization: "some_organization"
12 | retentionPolicy: "some_policy"
13 | url: "http://some/url"
14 | usingJenkinsProxy: true
15 |
--------------------------------------------------------------------------------
/src/main/resources/jenkinsci/plugins/influxdb/InfluxDbGlobalConfig/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/.github/workflows/jenkins-security-scan.yml:
--------------------------------------------------------------------------------
1 | name: Jenkins Security Scan
2 |
3 | on:
4 | push:
5 | branches:
6 | - development
7 | pull_request:
8 | types: [ opened, synchronize, reopened ]
9 | workflow_dispatch:
10 |
11 | permissions:
12 | security-events: write
13 | contents: read
14 | actions: read
15 |
16 | jobs:
17 | security-scan:
18 | uses: jenkins-infra/jenkins-security-scan/.github/workflows/jenkins-security-scan.yaml@v2
19 | with:
20 | java-cache: 'maven' # Optionally enable use of a build dependency cache. Specify 'maven' or 'gradle' as appropriate.
21 | # java-version: 21 # Optionally specify what version of Java to set up for the build, or remove to use a recent default.
22 |
--------------------------------------------------------------------------------
/src/main/java/jenkinsci/plugins/influxdb/generators/PointGenerator.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.generators;
2 |
3 | import hudson.model.Run;
4 | import jenkinsci.plugins.influxdb.models.AbstractPoint;
5 |
6 | public interface PointGenerator {
7 |
8 | boolean hasReport();
9 |
10 | AbstractPoint[] generate();
11 |
12 | /**
13 | * Initializes a basic build point with the basic data already set with a specified timestamp.
14 | */
15 | AbstractPoint buildPoint(String name, String customPrefix, Run, ?> build, long timeStamp);
16 |
17 | /**
18 | * Initializes a basic build point with the basic data already set.
19 | */
20 | AbstractPoint buildPoint(String name, String customPrefix, Run, ?> build);
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/jenkinsci/plugins/influxdb/InfluxReportException.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb;
2 |
3 | /**
4 | * Generic Exception thrown whenever an Exception occurs writing to InfluxDB.
5 | */
6 | public class InfluxReportException extends RuntimeException {
7 |
8 | public InfluxReportException(String message) {
9 | super(message);
10 | }
11 |
12 | public InfluxReportException(String message, Throwable cause) {
13 | super(message, cause);
14 | }
15 |
16 | public InfluxReportException(Throwable cause) {
17 | super(cause);
18 | }
19 |
20 | public InfluxReportException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
21 | super(message, cause, enableSuppression, writableStackTrace);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/resources/jenkinsci/plugins/influxdb/InfluxDbStep/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/main/resources/jenkinsci/plugins/influxdb/InfluxDbPublisher/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/test/java/jenkinsci/plugins/influxdb/generators/serenity/SerenityCannedJsonSummaryFile.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.generators.serenity;
2 |
3 | import java.io.IOException;
4 | import java.net.URISyntaxException;
5 | import java.nio.file.Files;
6 | import java.nio.file.Path;
7 | import java.nio.file.Paths;
8 |
9 | public class SerenityCannedJsonSummaryFile implements ISerenityJsonSummaryFile {
10 |
11 | public SerenityCannedJsonSummaryFile() {}
12 |
13 | public boolean exists() {
14 | return Files.exists(getPath());
15 | }
16 |
17 | public Path getPath() {
18 | try {
19 | return Paths.get(ClassLoader.getSystemResource("serenity/serenity-summary.json").toURI());
20 | } catch (URISyntaxException e) {
21 | //
22 | }
23 | return null;
24 | }
25 |
26 | public String getContents() throws IOException {
27 | return Files.readString(getPath());
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016- Eficode Ltd
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/main/java/jenkinsci/plugins/influxdb/generators/serenity/SerenityJsonSummaryFile.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.generators.serenity;
2 |
3 | import java.io.IOException;
4 | import java.nio.file.Files;
5 | import java.nio.file.Path;
6 |
7 | public class SerenityJsonSummaryFile implements ISerenityJsonSummaryFile {
8 |
9 | private static final String SERENITY_OUTPUT_DIRECTORY = "target/site/serenity";
10 | private static final String SERENITY_JSON_SUMMARY_FILE = "serenity-summary.json";
11 |
12 | private final String workspace;
13 |
14 | public SerenityJsonSummaryFile(String workspace) {
15 | this.workspace = workspace;
16 | }
17 |
18 | public boolean exists() {
19 | try {
20 | return Files.exists(getPath());
21 | } catch (IllegalArgumentException e) {
22 | return false;
23 | }
24 | }
25 |
26 | public Path getPath() {
27 | if (workspace == null) {
28 | throw new IllegalArgumentException("no workspace");
29 | }
30 | return java.nio.file.Paths.get(workspace, SERENITY_OUTPUT_DIRECTORY, SERENITY_JSON_SUMMARY_FILE);
31 | }
32 |
33 | public String getContents() throws IOException {
34 | return Files.readString(getPath());
35 | }
36 | }
--------------------------------------------------------------------------------
/src/main/java/jenkinsci/plugins/influxdb/renderer/ProjectNameRenderer.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.renderer;
2 |
3 | import hudson.model.Run;
4 | import org.apache.commons.lang3.StringUtils;
5 |
6 | import java.util.Objects;
7 | import java.util.stream.Collectors;
8 | import java.util.stream.Stream;
9 |
10 | public class ProjectNameRenderer implements MeasurementRenderer> {
11 |
12 | private final String customPrefix;
13 | private final String customProjectName;
14 |
15 | public ProjectNameRenderer(String customPrefix, String customProjectName) {
16 | this.customPrefix = StringUtils.trimToNull(customPrefix);
17 | this.customProjectName = StringUtils.trimToNull(customProjectName);
18 | }
19 |
20 | @Override
21 | public String render(Run, ?> input) {
22 | return projectName(customPrefix, customProjectName, input);
23 | }
24 |
25 | private String projectName(String prefix, String projectName, Run, ?> build) {
26 | if (projectName == null) {
27 | projectName = StringUtils.trimToNull(build.getParent().getName());
28 | }
29 |
30 | return Stream.of(prefix, projectName)
31 | .filter(Objects::nonNull)
32 | .collect(Collectors.joining("_"));
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/resources/jenkinsci/plugins/influxdb/models/Target/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/src/main/java/jenkinsci/plugins/influxdb/InfluxDbStepExecution.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb;
2 |
3 | import hudson.EnvVars;
4 | import hudson.FilePath;
5 | import hudson.Launcher;
6 | import hudson.model.Run;
7 | import hudson.model.TaskListener;
8 | import org.jenkinsci.plugins.workflow.steps.StepContext;
9 | import org.jenkinsci.plugins.workflow.steps.SynchronousNonBlockingStepExecution;
10 |
11 | public class InfluxDbStepExecution extends SynchronousNonBlockingStepExecution {
12 |
13 | private static final long serialVersionUID = 1L;
14 |
15 | private transient final InfluxDbStep step;
16 |
17 | InfluxDbStepExecution(InfluxDbStep step, StepContext context) {
18 | super(context);
19 | this.step = step;
20 | }
21 |
22 | @Override
23 | protected Void run() throws Exception {
24 | FilePath workspace = getContext().get(FilePath.class);
25 | InfluxDbPublisher publisher = new InfluxDbPublisher(step.getSelectedTarget());
26 | publisher.setCustomData(step.getCustomData());
27 | publisher.setCustomDataMap(step.getCustomDataMap());
28 | publisher.setCustomDataMapTags(step.getCustomDataMapTags());
29 | publisher.setCustomDataTags(step.getCustomDataTags());
30 | publisher.setCustomPrefix(step.getCustomPrefix());
31 | publisher.setCustomProjectName(step.getCustomProjectName());
32 | publisher.setJenkinsEnvParameterField(step.getJenkinsEnvParameterField());
33 | publisher.setJenkinsEnvParameterTag(step.getJenkinsEnvParameterTag());
34 | publisher.setMeasurementName(step.getMeasurementName());
35 | publisher.setEnv(getContext().get(EnvVars.class));
36 |
37 | publisher.perform(getContext().get(Run.class), workspace, getContext().get(EnvVars.class), getContext().get(Launcher.class), getContext().get(TaskListener.class));
38 | return null;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/test/java/jenkinsci/plugins/influxdb/generators/serenity/SerenityJsonSummaryFileTest.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.generators.serenity;
2 |
3 | import static org.junit.jupiter.api.Assertions.*;
4 |
5 | import java.io.File;
6 | import java.io.IOException;
7 | import java.nio.charset.StandardCharsets;
8 | import java.nio.file.Files;
9 | import java.nio.file.Path;
10 | import org.junit.jupiter.api.Test;
11 | import org.junit.jupiter.api.io.TempDir;
12 |
13 | class SerenityJsonSummaryFileTest {
14 | @TempDir
15 | private File temporaryFolder;
16 |
17 | @Test
18 | void testAvailableSummary() throws Exception {
19 | writeToTemporaryPath("target/site/serenity/serenity-summary.json", "expected summary content");
20 |
21 | SerenityJsonSummaryFile serenityJsonSummaryFile = new SerenityJsonSummaryFile(pathOfTemporaryFolder());
22 |
23 | assertTrue(serenityJsonSummaryFile.exists());
24 | assertEquals("expected summary content", serenityJsonSummaryFile.getContents());
25 | }
26 |
27 | @Test
28 | void testUnavailableSummary() {
29 | SerenityJsonSummaryFile serenityJsonSummaryFile = new SerenityJsonSummaryFile(pathOfTemporaryFolder());
30 |
31 | assertFalse(serenityJsonSummaryFile.exists());
32 | }
33 |
34 | @Test
35 | void testUnavailableWorkspace() {
36 | SerenityJsonSummaryFile serenityJsonSummaryFile = new SerenityJsonSummaryFile(null);
37 |
38 | assertFalse(serenityJsonSummaryFile.exists());
39 | }
40 |
41 | private void writeToTemporaryPath(String path, String content) throws IOException {
42 | Path temporaryPath = temporaryFolder.toPath();
43 | Path summaryJson = temporaryPath.resolve(path);
44 | Files.createDirectories(summaryJson.getParent());
45 | Files.writeString(summaryJson, content, StandardCharsets.UTF_8);
46 | }
47 |
48 | private String pathOfTemporaryFolder() {
49 | return temporaryFolder.getAbsolutePath();
50 | }
51 | }
--------------------------------------------------------------------------------
/src/test/resources/jenkinsci/plugins/influxdb/IntegrationBaseTest/configuration-as-code.yml:
--------------------------------------------------------------------------------
1 | jenkins:
2 | systemMessage: "Jenkins with JCasC provisioned InfluxDB instances"
3 | log:
4 | recorders:
5 | - loggers:
6 | - level: "FINE"
7 | name: "jenkinsci.plugins.influxdb.*"
8 | name: "InfluxDB"
9 |
10 | unclassified:
11 | influxDbGlobalConfig:
12 | targets:
13 | - credentialsId: "influxdb-v1-credentials"
14 | database: "${INFLUXDB_DATABASE_OR_BUCKET}"
15 | description: "InfluxDB v1"
16 | exposeExceptions: true
17 | globalListener: true
18 | jobScheduledTimeAsPointsTimestamp: true
19 | url: "${INFLUXDB_V1_URL}"
20 |
21 | - credentialsId: "influxdb-v2-credentials"
22 | database: "${INFLUXDB_DATABASE_OR_BUCKET}"
23 | description: "InfluxDB v2"
24 | exposeExceptions: true
25 | globalListener: true
26 | organization: "${INFLUXDB_ORGANIZATION}"
27 | jobScheduledTimeAsPointsTimestamp: true
28 | url: "${INFLUXDB_V2_URL}"
29 |
30 | - credentialsId: "influxdb-v3-credentials"
31 | database: "${INFLUXDB_DATABASE_OR_BUCKET}"
32 | description: "InfluxDB v3"
33 | exposeExceptions: true
34 | globalListener: true
35 | jobScheduledTimeAsPointsTimestamp: true
36 | url: "${INFLUXDB_V3_URL}"
37 |
38 | credentials:
39 | system:
40 | domainCredentials:
41 | - credentials:
42 | - usernamePassword:
43 | scope: GLOBAL
44 | id: "influxdb-v1-credentials"
45 | username: "${INFLUXDB_USERNAME}"
46 | password: "${INFLUXDB_PASSWORD}"
47 | description: "Username/Password for InfluxDB v1"
48 | - string:
49 | scope: GLOBAL
50 | id: "influxdb-v2-credentials"
51 | secret: "${INFLUXDB_V2_TOKEN}"
52 | description: "Token for InfluxDB v2"
53 | - string:
54 | scope: GLOBAL
55 | id: "influxdb-v3-credentials"
56 | secret: "${INFLUXDB_V3_TOKEN}"
57 | description: "Token for InfluxDB v3"
--------------------------------------------------------------------------------
/src/main/java/jenkinsci/plugins/influxdb/generators/CustomDataMapPointGenerator.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.generators;
2 |
3 | import hudson.model.Run;
4 | import hudson.model.TaskListener;
5 | import jenkinsci.plugins.influxdb.models.AbstractPoint;
6 | import jenkinsci.plugins.influxdb.renderer.ProjectNameRenderer;
7 |
8 | import java.util.ArrayList;
9 | import java.util.List;
10 | import java.util.Map;
11 |
12 | public class CustomDataMapPointGenerator extends AbstractPointGenerator {
13 |
14 | private final String customPrefix;
15 | private final Map> customDataMap;
16 | private final Map> customDataMapTags;
17 |
18 | public CustomDataMapPointGenerator(Run, ?> build, TaskListener listener,
19 | ProjectNameRenderer projectNameRenderer,
20 | long timestamp, String jenkinsEnvParameterTag,
21 | String customPrefix, Map> customDataMap,
22 | Map> customDataMapTags) {
23 | super(build, listener, projectNameRenderer, timestamp, jenkinsEnvParameterTag);
24 | this.customPrefix = customPrefix;
25 | this.customDataMap = customDataMap;
26 | this.customDataMapTags = customDataMapTags;
27 | }
28 |
29 | public boolean hasReport() {
30 | return (customDataMap != null && customDataMap.size() > 0);
31 | }
32 |
33 | public AbstractPoint[] generate() {
34 | List points = new ArrayList<>();
35 |
36 | for (Map.Entry> entry : customDataMap.entrySet()) {
37 | AbstractPoint point = buildPoint(entry.getKey(), customPrefix, build).addFields(entry.getValue());
38 |
39 | if (customDataMapTags != null) {
40 | Map customTags = customDataMapTags.get(entry.getKey());
41 | if (customTags != null) {
42 | if (customTags.size() > 0) {
43 | point.addTags(customTags);
44 | }
45 | }
46 | }
47 |
48 |
49 | points.add(point);
50 | }
51 |
52 | return points.toArray(new AbstractPoint[0]);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/jenkinsci/plugins/influxdb/generators/CustomDataPointGenerator.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.generators;
2 |
3 | import hudson.model.Run;
4 | import hudson.model.TaskListener;
5 | import jenkinsci.plugins.influxdb.models.AbstractPoint;
6 | import jenkinsci.plugins.influxdb.renderer.ProjectNameRenderer;
7 |
8 | import java.util.Map;
9 |
10 | import static jenkinsci.plugins.influxdb.InfluxDbPublisher.DEFAULT_MEASUREMENT_NAME;
11 |
12 | public class CustomDataPointGenerator extends AbstractPointGenerator {
13 |
14 | private static final String BUILD_TIME = "build_time";
15 |
16 | private final String customPrefix;
17 | private final String measurementName;
18 | private final Map customData;
19 | private final Map customDataTags;
20 |
21 | public CustomDataPointGenerator(Run, ?> build, TaskListener listener,
22 | ProjectNameRenderer projectNameRenderer,
23 | long timestamp, String jenkinsEnvParameterTag,
24 | String customPrefix, Map customData,
25 | Map customDataTags, String measurementName) {
26 | super(build, listener, projectNameRenderer, timestamp, jenkinsEnvParameterTag);
27 | this.customPrefix = customPrefix;
28 | this.customData = customData;
29 | this.customDataTags = customDataTags;
30 | // Extra logic to retain compatibility with existing "jenkins_custom_data" tables
31 | this.measurementName = DEFAULT_MEASUREMENT_NAME.equals(measurementName) ? "jenkins_custom_data" : "custom_" + measurementName;
32 | }
33 |
34 | public boolean hasReport() {
35 | return (customData != null && !customData.isEmpty());
36 | }
37 |
38 | public AbstractPoint[] generate() {
39 | long startTime = build.getTimeInMillis();
40 | long currTime = System.currentTimeMillis();
41 | long dt = currTime - startTime;
42 |
43 | AbstractPoint point = buildPoint(measurementName, customPrefix, build)
44 | .addField(BUILD_TIME, build.getDuration() == 0 ? dt : build.getDuration())
45 | .addFields(customData);
46 |
47 | if (customDataTags != null) {
48 | if (!customDataTags.isEmpty()) {
49 | point.addTags(customDataTags);
50 | }
51 | }
52 |
53 |
54 | return new AbstractPoint[]{point};
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/doc/breaking_changes.md:
--------------------------------------------------------------------------------
1 | # Breaking Changes
2 |
3 | ## 6.0
4 |
5 | - Fields `project_name` and `project_path` have been removed from **all metrics** since they are already defined as
6 | tags.
7 | - Field `build_result` has been removed from the **jenkins_data** metrics since it is already defined as tag.
8 |
9 | ## 5.0
10 |
11 | - Changes to Robt Framework metrics:
12 | - `rf_critical_pass_percentage` has been removed. It has been replaced by `rf_pass_percentage_total`,
13 | which sends the percentage of tests that passed including skipped tests.
14 |
15 | ## 4.0
16 |
17 | - Changes to Robot Framework metrics:
18 | - All `rf_critical_*` metrics have been removed as criticality has been deprecated by the Robot Framework plugin.
19 | - EXCEPT `rf_critical_pass_percentage`. It now sends the percentage of tests that passed including skipped tests.
20 | - `rf_pass_percentage` continues behaviour as before and sends the percentage of tests that passed excluding skipped tests.
21 |
22 | ## 3.0
23 |
24 | - InfluxDB 1.7 and lower are no longer supported. Only supported 1.x version is 1.8.x.
25 | - "username" and "password" are no longer used when defining new InfluxDB Targets.
26 | Credentials are used instead. Please check your InfluxDB Target configurations from
27 | Manage Jenkins --> Configure System.
28 | - All pipelines that create a new Target inside
29 | the pipeline need to be modified so, that they use `target.credentialsId` instead of
30 | `target.username` and `target.password`.
31 | - JCasC configurations need to be modified, so that they use `credentialsId` instead of `username`
32 | and `password`.
33 | - JUnit `test_name` field/tag changed to remove pipeline name. If your pipeline had multiple `junit` steps,
34 | the pipeline step information is now recorded in the `pipeline_step` field/tag. For example:
35 | - Before
`test_name`: `Tests / Test Stage 1 / my_test_name`
36 | - After
`test_name`: `my_test_name`
`test_full_class_name`: `mypackage.MyClass`
`pipeline_step`: `Tests / Test Stage 1`
37 |
38 |
39 | ## 2.0
40 |
41 | - From version 2.0 onwards `selectedTarget` is a **mandatory** parameter
42 | for pipelines and the `target` parameter is no longer supported.
43 | - Configuration As Code: the configuration needs to be changed from
44 | `influxDbPublisher` to `influxDbGlobalConfig`.
45 | - Might cause issues when creating new targets in pipelines. The
46 | `InfluxDbPublisher` instance is now
47 | under jenkinsci.plugins.influxdb.**InfluxDbStep**.DescriptorImpl.
48 |
49 | ## 1.13
50 |
51 | From version 1.13 onwards different plugins are listed as optional
52 | dependencies. In order to get rid of mandatory dependency errors,
53 | the InfluxDB plugin must be re-installed.
--------------------------------------------------------------------------------
/src/main/java/jenkinsci/plugins/influxdb/generators/JacocoPointGenerator.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.generators;
2 |
3 | import hudson.model.Run;
4 | import hudson.model.TaskListener;
5 | import hudson.plugins.jacoco.JacocoBuildAction;
6 | import hudson.plugins.jacoco.model.Coverage;
7 | import jenkinsci.plugins.influxdb.models.AbstractPoint;
8 | import jenkinsci.plugins.influxdb.renderer.ProjectNameRenderer;
9 |
10 | public class JacocoPointGenerator extends AbstractPointGenerator {
11 | private static final String JACOCO_CLASS = "jacoco_class";
12 | private static final String JACOCO_LINE = "jacoco_line";
13 | private static final String JACOCO_BRANCH = "jacoco_branch";
14 | private static final String JACOCO_METHOD = "jacoco_method";
15 | private static final String JACOCO_INSTRUCTION = "jacoco_instruction";
16 | private static final String JACOCO_COMPLEXITY = "jacoco_complexity";
17 |
18 | private final String customPrefix;
19 | private final JacocoBuildAction jacocoBuildAction;
20 |
21 | public JacocoPointGenerator(Run, ?> build, TaskListener listener,
22 | ProjectNameRenderer projectNameRenderer,
23 | long timestamp, String jenkinsEnvParameterTag,
24 | String customPrefix) {
25 | super(build, listener, projectNameRenderer, timestamp, jenkinsEnvParameterTag);
26 | this.customPrefix = customPrefix;
27 | jacocoBuildAction = build.getAction(JacocoBuildAction.class);
28 | }
29 |
30 | public boolean hasReport() {
31 | return jacocoBuildAction != null && jacocoBuildAction.getResult() != null;
32 | }
33 |
34 | public AbstractPoint[] generate() {
35 |
36 | AbstractPoint point = buildPoint("jacoco_data", customPrefix, build);
37 | addFields(point, JACOCO_CLASS, jacocoBuildAction.getResult().getClassCoverage());
38 | addFields(point, JACOCO_LINE, jacocoBuildAction.getResult().getLineCoverage());
39 | addFields(point, JACOCO_BRANCH, jacocoBuildAction.getResult().getBranchCoverage());
40 | addFields(point, JACOCO_METHOD, jacocoBuildAction.getResult().getMethodCoverage());
41 | addFields(point, JACOCO_INSTRUCTION, jacocoBuildAction.getResult().getInstructionCoverage());
42 | addFields(point, JACOCO_COMPLEXITY, jacocoBuildAction.getResult().getComplexityScore());
43 |
44 | return new AbstractPoint[]{point};
45 | }
46 |
47 | private void addFields(AbstractPoint point, String prefix, Coverage coverage) {
48 | point.addField(prefix + "_coverage_rate", coverage.getPercentageFloat());
49 | point.addField(prefix + "_covered", coverage.getCovered());
50 | point.addField(prefix + "_missed", coverage.getMissed());
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/jenkinsci/plugins/influxdb/generators/MetricsPointGenerator.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.generators;
2 |
3 | import hudson.model.Run;
4 | import hudson.model.TaskListener;
5 | import jenkins.metrics.impl.TimeInQueueAction;
6 | import jenkinsci.plugins.influxdb.models.AbstractPoint;
7 | import jenkinsci.plugins.influxdb.renderer.ProjectNameRenderer;
8 |
9 | public class MetricsPointGenerator extends AbstractPointGenerator {
10 | private static final String BLOCKED_TIME = "blocked_time";
11 | private static final String BUILDABLE_TIME = "buildable_time";
12 | private static final String BUILDING_TIME = "building_time";
13 | private static final String EXECUTING_TIME = "executing_time";
14 | private static final String EXECUTOR_UTILIZATION = "executor_utilization";
15 | private static final String QUEUEING_TIME = "queue_time";
16 | private static final String SUBTASK_COUNT = "subtask_count";
17 | private static final String TOTAL_DURATION = "total_duration";
18 | private static final String WAITING_TIME = "waiting_time";
19 |
20 | private final Run, ?> build;
21 | private final String customPrefix;
22 | private final TimeInQueueAction timeInQueueAction;
23 |
24 | public MetricsPointGenerator(Run, ?> build, TaskListener listener,
25 | ProjectNameRenderer projectNameRenderer,
26 | long timestamp, String jenkinsEnvParameterTag,
27 | String customPrefix) {
28 | super(build, listener, projectNameRenderer, timestamp, jenkinsEnvParameterTag);
29 | this.build = build;
30 | this.customPrefix = customPrefix;
31 | timeInQueueAction = build.getAction(TimeInQueueAction.class);
32 | }
33 |
34 | public boolean hasReport() {
35 | return timeInQueueAction != null;
36 | }
37 |
38 | public AbstractPoint[] generate() {
39 | AbstractPoint point = buildPoint("metrics_data", customPrefix, build);
40 | point.addField(BLOCKED_TIME, timeInQueueAction.getBlockedDurationMillis());
41 | point.addField(BUILDABLE_TIME, timeInQueueAction.getBuildableDurationMillis());
42 | point.addField(BUILDING_TIME, timeInQueueAction.getBuildingDurationMillis());
43 | point.addField(EXECUTING_TIME, timeInQueueAction.getExecutingTimeMillis());
44 | point.addField(EXECUTOR_UTILIZATION, timeInQueueAction.getExecutorUtilization());
45 | point.addField(QUEUEING_TIME, timeInQueueAction.getQueuingDurationMillis());
46 | point.addField(SUBTASK_COUNT, timeInQueueAction.getSubTaskCount());
47 | point.addField(TOTAL_DURATION, timeInQueueAction.getTotalDurationMillis());
48 | point.addField(WAITING_TIME, timeInQueueAction.getWaitingDurationMillis());
49 | return new AbstractPoint[]{point};
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/jenkinsci/plugins/influxdb/InfluxDbGlobalConfig.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb;
2 |
3 | import hudson.Extension;
4 | import hudson.ExtensionList;
5 | import hudson.init.InitMilestone;
6 | import hudson.init.Initializer;
7 | import jenkins.model.GlobalConfiguration;
8 | import jenkinsci.plugins.influxdb.models.Target;
9 | import net.sf.json.JSONObject;
10 | import org.kohsuke.stapler.StaplerRequest2;
11 |
12 | import java.util.Arrays;
13 | import java.util.Collections;
14 | import java.util.List;
15 | import java.util.Optional;
16 | import java.util.concurrent.CopyOnWriteArrayList;
17 |
18 | @Extension
19 | public class InfluxDbGlobalConfig extends GlobalConfiguration {
20 |
21 | private List targets = new CopyOnWriteArrayList<>();
22 | private boolean targetsMigrated = false;
23 |
24 | public InfluxDbGlobalConfig() {
25 | load();
26 | }
27 |
28 | public static InfluxDbGlobalConfig getInstance() {
29 | return GlobalConfiguration.all().get(InfluxDbGlobalConfig.class);
30 | }
31 |
32 | public List getTargets() {
33 | return Collections.unmodifiableList(targets);
34 | }
35 |
36 | public void setTargets(List targets) {
37 | this.targets = targets;
38 | save();
39 | }
40 |
41 | @SuppressWarnings("deprecation")
42 | @Initializer(after = InitMilestone.JOB_LOADED)
43 | public void migrateTargets() {
44 | if (targetsMigrated) {
45 | return;
46 | }
47 | Optional optionalDescriptor = ExtensionList.lookup(InfluxDbPublisher.DescriptorImpl.class).stream().findFirst();
48 |
49 | optionalDescriptor.ifPresent(publisher -> {
50 | if (publisher.getDeprecatedTargets().length > 0) {
51 | targets = Arrays.asList(publisher.getDeprecatedTargets());
52 | save();
53 | }
54 | publisher.removeDeprecatedTargets();
55 | });
56 | targetsMigrated = true;
57 | save();
58 | }
59 |
60 | @Override
61 | public boolean configure(StaplerRequest2 req, JSONObject formData) {
62 | targets = new CopyOnWriteArrayList<>();
63 | targets.addAll(req.bindJSONToList(Target.class, formData.get("targets")));
64 | save();
65 | return true;
66 | }
67 |
68 | /**
69 | * Add target to list of targets
70 | *
71 | * @param target Target to add
72 | */
73 | public void addTarget(Target target) {
74 | targets.add(target);
75 | }
76 |
77 | /**
78 | * Remove target from list of targets
79 | *
80 | * @param targetDescription Target description of target to remove.
81 | */
82 | public void removeTarget(String targetDescription) {
83 | targets.removeIf(target -> target.getDescription().equals(targetDescription));
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/test/java/jenkinsci/plugins/influxdb/renderer/ProjectNameRendererTest.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.renderer;
2 |
3 | import hudson.model.Job;
4 | import hudson.model.Run;
5 | import org.junit.jupiter.api.BeforeEach;
6 | import org.junit.jupiter.api.Test;
7 | import org.mockito.Mockito;
8 |
9 | import static org.junit.jupiter.api.Assertions.assertTrue;
10 |
11 | class ProjectNameRendererTest {
12 |
13 | private static final String JOB_NAME = "master";
14 | private static final int BUILD_NUMBER = 11;
15 | private static final String CUSTOM_PREFIX = "test_prefix";
16 | private static final String CUSTOM_PROJECT_NAME = "test_projectname";
17 |
18 | private Run,?> build;
19 | private Job job;
20 |
21 | @BeforeEach
22 | void before() {
23 | build = Mockito.mock(Run.class);
24 | job = Mockito.mock(Job.class);
25 |
26 | Mockito.when(build.getNumber()).thenReturn(BUILD_NUMBER);
27 | Mockito.doReturn(job).when(build).getParent();
28 | Mockito.when(job.getName()).thenReturn(JOB_NAME);
29 | }
30 |
31 | @Test
32 | void customProjectNameWithCustomPrefix() {
33 | ProjectNameRenderer projectNameRenderer = new ProjectNameRenderer(CUSTOM_PREFIX, CUSTOM_PROJECT_NAME);
34 | String renderedProjectName = projectNameRenderer.render(build);
35 | assertTrue(renderedProjectName.startsWith(CUSTOM_PREFIX + "_" + CUSTOM_PROJECT_NAME));
36 | }
37 |
38 | @Test
39 | void customProjectNameWithNullPrefix() {
40 | ProjectNameRenderer projectNameRenderer = new ProjectNameRenderer(null, CUSTOM_PROJECT_NAME);
41 | String renderedProjectName = projectNameRenderer.render(build);
42 | assertTrue(renderedProjectName.startsWith(CUSTOM_PROJECT_NAME));
43 | }
44 |
45 | @Test
46 | void nullProjectNameWithCustomPrefix() {
47 | ProjectNameRenderer projectNameRenderer = new ProjectNameRenderer(CUSTOM_PREFIX, null);
48 | String renderedProjectName = projectNameRenderer.render(build);
49 | assertTrue(renderedProjectName.startsWith(CUSTOM_PREFIX + "_" + JOB_NAME));
50 | }
51 |
52 | @Test
53 | void nullProjectNameWithNullPrefix() {
54 | ProjectNameRenderer projectNameRenderer = new ProjectNameRenderer(null, null);
55 | String renderedProjectName = projectNameRenderer.render(build);
56 | assertTrue(renderedProjectName.startsWith(JOB_NAME));
57 | }
58 |
59 | @Test
60 | void nullProjectNameWithNullPrefix_NoSideEffects() {
61 | ProjectNameRenderer projectNameRenderer = new ProjectNameRenderer(null, null);
62 |
63 | Mockito.when(job.getName()).thenReturn("job 1");
64 | String renderedProjectName1 = projectNameRenderer.render(build);
65 | assertTrue(renderedProjectName1.startsWith("job 1"));
66 |
67 | Mockito.when(job.getName()).thenReturn("job 2");
68 | String renderedProjectName2 = projectNameRenderer.render(build);
69 | assertTrue(renderedProjectName2.startsWith("job 2"));
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ development ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ development ]
20 | schedule:
21 | - cron: '30 1 * * 0'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 | permissions:
28 | actions: read
29 | contents: read
30 | security-events: write
31 |
32 | strategy:
33 | fail-fast: false
34 | matrix:
35 | language: [ 'java' ]
36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
38 |
39 | steps:
40 | - name: Checkout repository
41 | uses: actions/checkout@v3
42 |
43 | # Initializes the CodeQL tools for scanning.
44 | - name: Initialize CodeQL
45 | uses: github/codeql-action/init@v2
46 | with:
47 | languages: ${{ matrix.language }}
48 | # If you wish to specify custom queries, you can do so here or in a config file.
49 | # By default, queries listed here will override any specified in a config file.
50 | # Prefix the list here with "+" to use these queries and those in the config file.
51 |
52 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
53 | # queries: security-extended,security-and-quality
54 |
55 |
56 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
57 | # If this step fails, then you should remove it and run the build manually (see below)
58 | - name: Autobuild
59 | uses: github/codeql-action/autobuild@v2
60 |
61 | # ℹ️ Command-line programs to run using the OS shell.
62 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
63 |
64 | # If the Autobuild fails above, remove it and uncomment the following three lines.
65 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
66 |
67 | # - run: |
68 | # echo "Run, Build Application using script"
69 | # ./location_of_script_within_repo/buildscript.sh
70 |
71 | - name: Perform CodeQL Analysis
72 | uses: github/codeql-action/analyze@v2
73 |
--------------------------------------------------------------------------------
/src/main/java/jenkinsci/plugins/influxdb/generators/GitPointGenerator.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.generators;
2 |
3 | import hudson.model.Run;
4 | import hudson.model.TaskListener;
5 | import hudson.plugins.git.Branch;
6 | import hudson.plugins.git.Revision;
7 | import hudson.plugins.git.util.BuildData;
8 | import jenkinsci.plugins.influxdb.models.AbstractPoint;
9 | import jenkinsci.plugins.influxdb.renderer.ProjectNameRenderer;
10 | import org.apache.commons.collections.CollectionUtils;
11 |
12 | import java.util.ArrayList;
13 | import java.util.Collection;
14 | import java.util.List;
15 |
16 | /**
17 | * @author Mathieu Delrocq
18 | */
19 | public class GitPointGenerator extends AbstractPointGenerator {
20 |
21 | // Point fields names
22 | protected static final String GIT_REPOSITORY = "git_repository";
23 | protected static final String GIT_REVISION = "git_revision";
24 | protected static final String GIT_REFERENCE = "git_reference";
25 | protected static final String UNIQUE_ID = "unique_id";
26 |
27 | private String customPrefix;
28 | private List gitActions;
29 |
30 | public GitPointGenerator(Run, ?> build, TaskListener listener, ProjectNameRenderer projectNameRenderer,
31 | long timestamp, String jenkinsEnvParameterTag, String customPrefix) {
32 | super(build, listener, projectNameRenderer, timestamp, jenkinsEnvParameterTag);
33 | this.customPrefix = customPrefix;
34 | gitActions = build.getActions(BuildData.class);
35 | }
36 |
37 | /**
38 | * Check if git infos are presents in the build
39 | *
40 | * @return true if present
41 | */
42 | @Override
43 | public boolean hasReport() {
44 | return CollectionUtils.isNotEmpty(gitActions);
45 | }
46 |
47 | /**
48 | * Generates Git Points with datas in Git plugins
49 | *
50 | * @return Array of Point
51 | */
52 | @Override
53 | public AbstractPoint[] generate() {
54 | List points = new ArrayList<>();
55 | String sha1String = null;
56 | String branchName = null;
57 | BuildData gitAction = null;
58 | for (int i = 0; i < gitActions.size(); i++) {
59 | gitAction = gitActions.get(i);
60 | Revision revision = gitAction.getLastBuiltRevision();
61 | if (revision != null) {
62 | sha1String = revision.getSha1String();
63 | Collection branches = revision.getBranches();
64 | if (CollectionUtils.isNotEmpty(branches)) {
65 | branchName = branches.iterator().next().getName();
66 | }
67 | }
68 | AbstractPoint point = buildPoint("git_data", customPrefix, build)
69 | .addTag(UNIQUE_ID, String.valueOf(i + 1))
70 | .addField(GIT_REPOSITORY, !CollectionUtils.isEmpty(gitAction.getRemoteUrls()) ? gitAction.getRemoteUrls().iterator().next() : "")//
71 | .addField(GIT_REFERENCE, branchName)
72 | .addField(GIT_REVISION, sha1String);
73 | points.add(point);
74 | }
75 | return points.toArray(new AbstractPoint[0]);
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/src/test/java/jenkinsci/plugins/influxdb/InfluxDbPublisherTest.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb;
2 |
3 | import hudson.EnvVars;
4 | import hudson.FilePath;
5 | import hudson.Launcher;
6 | import hudson.model.FreeStyleProject;
7 | import hudson.model.Run;
8 | import hudson.model.TaskListener;
9 | import jenkins.model.Jenkins;
10 | import jenkinsci.plugins.influxdb.models.Target;
11 | import org.junit.jupiter.api.Test;
12 | import org.jvnet.hudson.test.Issue;
13 | import org.jvnet.hudson.test.JenkinsRule;
14 | import org.jvnet.hudson.test.junit.jupiter.WithJenkins;
15 | import org.mockito.Mock;
16 | import org.mockito.Mockito;
17 |
18 | import java.io.File;
19 | import java.util.Collections;
20 |
21 | import static org.junit.jupiter.api.Assertions.assertEquals;
22 | import static org.junit.jupiter.api.Assertions.assertThrows;
23 |
24 | @WithJenkins
25 | class InfluxDbPublisherTest {
26 |
27 | private FilePath workspace = new FilePath(new File("."));
28 | @Mock
29 | private Run build;
30 | @Mock
31 | private Launcher launcher;
32 | @Mock
33 | private TaskListener listener;
34 | @Mock
35 | private EnvVars envVars;
36 |
37 | @Test
38 | void testEmptyTargetShouldThrowException(JenkinsRule j) {
39 |
40 | InfluxDbPublisher.DescriptorImpl descriptorMock = Mockito.mock(InfluxDbPublisher.DescriptorImpl.class);
41 | Jenkins jenkinsMock = Mockito.mock(Jenkins.class);
42 | Mockito.when(descriptorMock.getTargets()).thenReturn(Collections.emptyList());
43 | Mockito.when(jenkinsMock.getDescriptorByType(InfluxDbPublisher.DescriptorImpl.class)).thenReturn(descriptorMock);
44 |
45 | assertThrows(RuntimeException.class, () -> new InfluxDbPublisher("").perform(build, workspace, envVars, launcher, listener), "Target was null!");
46 | }
47 |
48 | @Test
49 | @Issue("JENKINS-61305")
50 | void testConfigRoundTripShouldPreserveSelectedTarget(JenkinsRule j) throws Exception {
51 | InfluxDbGlobalConfig globalConfig = InfluxDbGlobalConfig.getInstance();
52 | Target target1 = new Target();
53 | target1.setDescription("Target1");
54 | Target target2 = new Target();
55 | target2.setDescription("Target2");
56 | globalConfig.addTarget(target1);
57 | globalConfig.addTarget(target2);
58 |
59 | InfluxDbPublisher before = new InfluxDbPublisher("Target2");
60 | assertEquals("Target2", before.getSelectedTarget());
61 | assertEquals(before.getTarget(), target2);
62 | FreeStyleProject project = j.createFreeStyleProject();
63 | project.getPublishersList().add(before);
64 | j.configRoundtrip(project);
65 |
66 | InfluxDbPublisher after = project.getPublishersList().get(InfluxDbPublisher.class);
67 | j.assertEqualBeans(before, after, "selectedTarget");
68 | }
69 |
70 | @Test
71 | void testGetTargetShouldReturnFirstTargetWithNull(JenkinsRule j) {
72 | InfluxDbGlobalConfig globalConfig = InfluxDbGlobalConfig.getInstance();
73 |
74 | Target target1 = new Target();
75 | target1.setDescription("Target1");
76 | globalConfig.addTarget(target1);
77 |
78 | InfluxDbPublisher publisher = new InfluxDbPublisher(null);
79 | assertEquals(target1, publisher.getTarget());
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/main/java/jenkinsci/plugins/influxdb/generators/CoveragePointGenerator.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.generators;
2 |
3 | import edu.hm.hafner.coverage.Metric;
4 | import hudson.model.Run;
5 | import hudson.model.TaskListener;
6 | import io.jenkins.plugins.coverage.metrics.model.Baseline;
7 | import io.jenkins.plugins.coverage.metrics.model.CoverageStatistics;
8 | import io.jenkins.plugins.coverage.metrics.model.ElementFormatter;
9 | import io.jenkins.plugins.coverage.metrics.steps.CoverageBuildAction;
10 | import jenkinsci.plugins.influxdb.models.AbstractPoint;
11 | import jenkinsci.plugins.influxdb.renderer.ProjectNameRenderer;
12 | import org.apache.commons.lang3.StringUtils;
13 |
14 | import java.text.NumberFormat;
15 | import java.text.ParseException;
16 | import java.util.ArrayList;
17 | import java.util.List;
18 |
19 |
20 | public class CoveragePointGenerator extends AbstractPointGenerator {
21 |
22 |
23 | private final String customPrefix;
24 |
25 | public CoveragePointGenerator(Run, ?> build,
26 | TaskListener listener,
27 | ProjectNameRenderer projectNameRenderer,
28 | long timestamp,
29 | String jenkinsEnvParameterTag,
30 | String customPrefix) {
31 | super(build, listener, projectNameRenderer, timestamp, jenkinsEnvParameterTag);
32 | this.customPrefix = customPrefix;
33 | }
34 |
35 | @Override
36 | public boolean hasReport() {
37 | return build.getAction(CoverageBuildAction.class) != null;
38 | }
39 |
40 | @Override
41 | public AbstractPoint[] generate() {
42 |
43 | List points = new ArrayList<>();
44 | CoverageBuildAction action = build.getAction(CoverageBuildAction.class);
45 | CoverageStatistics coverageStatistics = action.getStatistics();
46 | for (Baseline baseline : new Baseline[]{Baseline.PROJECT, Baseline.MODIFIED_LINES, Baseline.MODIFIED_FILES}) {
47 | points.add(buildSubPoint("coverage_" + baseline.toString().toLowerCase() + "_data", customPrefix, build, baseline, coverageStatistics));
48 | }
49 |
50 | return points.toArray(new AbstractPoint[0]);
51 | }
52 |
53 | private AbstractPoint buildSubPoint(String name, String customPrefix, Run, ?> build, Baseline baseline, CoverageStatistics coverageStatistics) {
54 | AbstractPoint point = buildPoint(name, customPrefix, build);
55 | ElementFormatter formatter = new ElementFormatter();
56 | for (Metric m : Metric.values()) {
57 | coverageStatistics.getValue(baseline, m).ifPresent(value -> {
58 | String x = formatter.format(value);
59 | try {
60 | Number number = NumberFormat.getInstance().parse(StringUtils.substringBefore(x, "%"));
61 | if (StringUtils.contains(x, "%")) { // failsafe to enforce percentage values as floats
62 | number = number.floatValue();
63 | }
64 | point.addField(m.toTagName(), number);
65 | } catch (ParseException e) {
66 | // No operation
67 | }
68 | });
69 | }
70 |
71 | return point;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/main/java/jenkinsci/plugins/influxdb/generators/CoberturaPointGenerator.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.generators;
2 |
3 | import hudson.model.Run;
4 | import hudson.model.TaskListener;
5 | import hudson.plugins.cobertura.CoberturaBuildAction;
6 | import hudson.plugins.cobertura.Ratio;
7 | import hudson.plugins.cobertura.targets.CoverageMetric;
8 | import hudson.plugins.cobertura.targets.CoverageResult;
9 | import jenkinsci.plugins.influxdb.models.AbstractPoint;
10 | import jenkinsci.plugins.influxdb.renderer.ProjectNameRenderer;
11 |
12 | public class CoberturaPointGenerator extends AbstractPointGenerator {
13 |
14 | private static final String COBERTURA_PACKAGE_COVERAGE_RATE = "cobertura_package_coverage_rate";
15 | private static final String COBERTURA_CLASS_COVERAGE_RATE = "cobertura_class_coverage_rate";
16 | private static final String COBERTURA_LINE_COVERAGE_RATE = "cobertura_line_coverage_rate";
17 | private static final String COBERTURA_BRANCH_COVERAGE_RATE = "cobertura_branch_coverage_rate";
18 | private static final String COBERTURA_NUMBER_OF_PACKAGES = "cobertura_number_of_packages";
19 | private static final String COBERTURA_NUMBER_OF_SOURCEFILES = "cobertura_number_of_sourcefiles";
20 | private static final String COBERTURA_NUMBER_OF_CLASSES = "cobertura_number_of_classes";
21 |
22 | private final CoberturaBuildAction coberturaBuildAction;
23 | private final String customPrefix;
24 |
25 | public CoberturaPointGenerator(Run, ?> build, TaskListener listener,
26 | ProjectNameRenderer projectNameRenderer,
27 | long timestamp, String jenkinsEnvParameterTag,
28 | String customPrefix) {
29 | super(build, listener, projectNameRenderer, timestamp, jenkinsEnvParameterTag);
30 | this.customPrefix = customPrefix;
31 | coberturaBuildAction = build.getAction(CoberturaBuildAction.class);
32 | }
33 |
34 | public boolean hasReport() {
35 | return coberturaBuildAction != null && coberturaBuildAction.getResult() != null;
36 | }
37 |
38 | public AbstractPoint[] generate() {
39 | CoverageResult result = coberturaBuildAction.getResult();
40 | Ratio conditionals = result.getCoverage(CoverageMetric.CONDITIONAL);
41 | Ratio lines = result.getCoverage(CoverageMetric.LINE);
42 | Ratio packages = result.getCoverage(CoverageMetric.PACKAGES);
43 | Ratio classes = result.getCoverage(CoverageMetric.CLASSES);
44 | Ratio files = result.getCoverage(CoverageMetric.FILES);
45 |
46 | AbstractPoint point = buildPoint("cobertura_data", customPrefix, build)
47 | .addField(COBERTURA_NUMBER_OF_PACKAGES, packages.denominator)
48 | .addField(COBERTURA_NUMBER_OF_SOURCEFILES, files.denominator)
49 | .addField(COBERTURA_NUMBER_OF_CLASSES, classes.denominator)
50 | .addField(COBERTURA_BRANCH_COVERAGE_RATE, conditionals.getPercentageFloat())
51 | .addField(COBERTURA_LINE_COVERAGE_RATE, lines.getPercentageFloat())
52 | .addField(COBERTURA_PACKAGE_COVERAGE_RATE, packages.getPercentageFloat())
53 | .addField(COBERTURA_CLASS_COVERAGE_RATE, classes.getPercentageFloat());
54 |
55 | return new AbstractPoint[]{point};
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/test/java/jenkinsci/plugins/influxdb/ConfigurationAsCodeTest.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb;
2 |
3 | import io.jenkins.plugins.casc.ConfigurationAsCode;
4 | import jenkinsci.plugins.influxdb.models.Target;
5 | import org.apache.commons.io.IOUtils;
6 | import org.junit.jupiter.api.Test;
7 | import org.jvnet.hudson.test.JenkinsRule;
8 | import org.jvnet.hudson.test.junit.jupiter.WithJenkins;
9 |
10 | import java.io.ByteArrayOutputStream;
11 | import java.io.InputStream;
12 | import java.nio.charset.StandardCharsets;
13 | import java.util.Collections;
14 |
15 | import static org.junit.jupiter.api.Assertions.assertTrue;
16 | import static org.junit.jupiter.api.Assertions.assertEquals;
17 |
18 | @WithJenkins
19 | class ConfigurationAsCodeTest {
20 |
21 | @Test
22 | void should_support_jcasc_from_yaml(JenkinsRule j) {
23 | InfluxDbGlobalConfig globalConfig = InfluxDbGlobalConfig.getInstance();
24 |
25 | String yamlUrl = getClass().getResource(getClass().getSimpleName() + "/configuration-as-code.yml").toString();
26 | ConfigurationAsCode.get().configure(yamlUrl);
27 |
28 | assertEquals(1, globalConfig.getTargets().size());
29 |
30 | Target target = globalConfig.getTargets().get(0);
31 | assertEquals("some description", target.getDescription());
32 | assertEquals("http://some/url", target.getUrl());
33 |
34 | assertEquals("some_id", target.getCredentialsId());
35 | assertEquals("some_database", target.getDatabase());
36 | assertEquals("some_policy", target.getRetentionPolicy());
37 | assertTrue(target.isJobScheduledTimeAsPointsTimestamp());
38 | assertTrue(target.isExposeExceptions());
39 | assertTrue(target.isUsingJenkinsProxy());
40 | assertTrue(target.isGlobalListener());
41 | assertEquals("some filter", target.getGlobalListenerFilter());
42 | assertEquals("some_organization", target.getOrganization());
43 | }
44 |
45 | @Test
46 | void should_support_jcasc_to_yaml(JenkinsRule j) throws Exception {
47 | InfluxDbGlobalConfig globalConfig = InfluxDbGlobalConfig.getInstance();
48 |
49 | Target target = new Target();
50 | target.setDescription("some description");
51 | target.setUrl("http://some/url");
52 | target.setCredentialsId("some_id");
53 | target.setDatabase("some_database");
54 | target.setRetentionPolicy("some_policy");
55 | target.setJobScheduledTimeAsPointsTimestamp(true);
56 | target.setExposeExceptions(true);
57 | target.setUsingJenkinsProxy(true);
58 | target.setGlobalListener(true);
59 | target.setGlobalListenerFilter("some filter");
60 | target.setOrganization("some_organization");
61 |
62 | globalConfig.setTargets(Collections.singletonList(target));
63 |
64 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
65 | ConfigurationAsCode.get().export(outputStream);
66 | String exportedYaml = outputStream.toString(StandardCharsets.UTF_8);
67 |
68 | InputStream yamlStream = getClass().getResourceAsStream(getClass().getSimpleName() + "/configuration-as-code.yml");
69 | String expectedYaml = IOUtils.toString(yamlStream, StandardCharsets.UTF_8)
70 | .replaceAll("\r\n?", "\n")
71 | .replace("unclassified:\n", "");
72 |
73 | assertTrue(exportedYaml.contains(expectedYaml));
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/jenkinsci/plugins/influxdb/generators/PerformancePointGenerator.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.generators;
2 |
3 | import hudson.model.Run;
4 | import hudson.model.TaskListener;
5 | import hudson.plugins.performance.actions.PerformanceBuildAction;
6 | import hudson.plugins.performance.reports.PerformanceReport;
7 | import jenkinsci.plugins.influxdb.models.AbstractPoint;
8 | import jenkinsci.plugins.influxdb.renderer.ProjectNameRenderer;
9 |
10 | import java.util.ArrayList;
11 | import java.util.List;
12 | import java.util.Map;
13 |
14 | public class PerformancePointGenerator extends AbstractPointGenerator {
15 |
16 | private static final String PERFORMANCE_ERROR_PERCENT = "error_percent"; // failed / size * 100
17 | private static final String PERFORMANCE_ERROR_COUNT = "error_count"; // Amount of failed samples
18 | private static final String PERFORMANCE_AVERAGE = "average"; // Total duration / size
19 | private static final String PERFORMANCE_90PERCENTILE = "90Percentile"; // 90 Percentile duration
20 | private static final String PERFORMANCE_MEDIAN = "median"; //median duration
21 | private static final String PERFORMANCE_MAX = "max"; // max duration
22 | private static final String PERFORMANCE_MIN = "min"; // min duration
23 | private static final String PERFORMANCE_TOTAL_TRAFFIC = "total_traffic";
24 | private static final String PERFORMANCE_SIZE = "size"; // Size of all samples
25 |
26 | private final String customPrefix;
27 | private final PerformanceBuildAction performanceBuildAction;
28 |
29 | public PerformancePointGenerator(Run, ?> build, TaskListener listener,
30 | ProjectNameRenderer projectNameRenderer,
31 | long timestamp, String jenkinsEnvParameterTag,
32 | String customPrefix) {
33 | super(build, listener, projectNameRenderer, timestamp, jenkinsEnvParameterTag);
34 | this.customPrefix = customPrefix;
35 | performanceBuildAction = build.getAction(PerformanceBuildAction.class);
36 | }
37 |
38 | public boolean hasReport() {
39 | return performanceBuildAction != null && performanceBuildAction.getPerformanceReportMap() != null;
40 | }
41 |
42 | public AbstractPoint[] generate() {
43 | Map reportMap = performanceBuildAction.getPerformanceReportMap().getPerformanceReportMap();
44 |
45 | List points = new ArrayList<>();
46 |
47 | for (PerformanceReport report : reportMap.values()) {
48 | points.add(generateReportPoint(report));
49 | }
50 |
51 | return points.toArray(new AbstractPoint[0]);
52 | }
53 |
54 | private AbstractPoint generateReportPoint(PerformanceReport performanceReport) {
55 | return buildPoint("performance_data", customPrefix, build)
56 | .addField(PERFORMANCE_ERROR_PERCENT, performanceReport.errorPercent())
57 | .addField(PERFORMANCE_ERROR_COUNT, performanceReport.countErrors())
58 | .addField(PERFORMANCE_AVERAGE, performanceReport.getAverage())
59 | .addField(PERFORMANCE_MAX, performanceReport.getMax())
60 | .addField(PERFORMANCE_MIN, performanceReport.getMin())
61 | .addField(PERFORMANCE_TOTAL_TRAFFIC, performanceReport.getTotalTrafficInKb())
62 | .addField(PERFORMANCE_SIZE, performanceReport.samplesCount())
63 | .addField(PERFORMANCE_90PERCENTILE, performanceReport.get90Line())
64 | .addField(PERFORMANCE_MEDIAN, performanceReport.getMedian());
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/test/java/jenkinsci/plugins/influxdb/generators/serenity/SerenityPointGeneratorTest.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.generators.serenity;
2 |
3 | import hudson.model.Job;
4 | import hudson.model.Run;
5 | import hudson.model.TaskListener;
6 | import jenkins.model.Jenkins;
7 | import jenkinsci.plugins.influxdb.generators.PointGeneratorBaseTest;
8 | import jenkinsci.plugins.influxdb.models.AbstractPoint;
9 | import jenkinsci.plugins.influxdb.renderer.ProjectNameRenderer;
10 | import org.apache.commons.lang3.StringUtils;
11 | import org.junit.jupiter.api.BeforeEach;
12 | import org.junit.jupiter.api.Test;
13 | import org.mockito.Mockito;
14 |
15 | import java.util.ArrayList;
16 | import java.util.Arrays;
17 | import java.util.List;
18 |
19 | import static org.junit.jupiter.api.Assertions.assertTrue;
20 |
21 | class SerenityPointGeneratorTest extends PointGeneratorBaseTest {
22 |
23 | private static final String JOB_NAME = "master";
24 | private static final int BUILD_NUMBER = 11;
25 | private static final String CUSTOM_PREFIX = "test_prefix";
26 | AbstractPoint point = null;
27 | private Run build;
28 | private ProjectNameRenderer measurementRenderer;
29 | private long currTime;
30 | private TaskListener listener;
31 |
32 | @BeforeEach
33 | void before() {
34 | build = Mockito.mock(Run.class);
35 | Job job = Mockito.mock(Job.class);
36 | listener = Mockito.mock(TaskListener.class);
37 | measurementRenderer = new ProjectNameRenderer(CUSTOM_PREFIX, null);
38 |
39 | Mockito.when(build.getNumber()).thenReturn(BUILD_NUMBER);
40 | Mockito.when(build.getParent()).thenReturn(job);
41 | Mockito.when(job.getName()).thenReturn(JOB_NAME);
42 | Mockito.when(job.getRelativeNameFrom(Mockito.nullable(Jenkins.class))).thenReturn("folder/" + JOB_NAME);
43 |
44 | currTime = System.currentTimeMillis();
45 |
46 | SerenityCannedJsonSummaryFile serenityCannedJsonSummaryFile = new SerenityCannedJsonSummaryFile();
47 | // build, listener, measurementRenderer, timestamp, jenkinsEnvParameterTag, customPrefix,
48 | SerenityPointGenerator serenityGen = new SerenityPointGenerator(build, listener, measurementRenderer, currTime,
49 | StringUtils.EMPTY, null, serenityCannedJsonSummaryFile);
50 |
51 | if (serenityGen.hasReport()) {
52 | List pointsToWrite = new ArrayList<>(Arrays.asList(serenityGen.generate()));
53 | // points.fields is private so just get all fields as a single string
54 | point = pointsToWrite.get(0);
55 | }
56 | }
57 |
58 | @Test
59 | void verifyResultsCounts() {
60 | assertTrue(allLineProtocolsContain(point, "serenity_results_counts_total=99"));
61 | assertTrue(allLineProtocolsContain(point, "serenity_results_counts_success=91"));
62 | assertTrue(allLineProtocolsContain(point, "serenity_results_counts_pending=8"));
63 | assertTrue(allLineProtocolsContain(point, "serenity_results_counts_error=0"));
64 | }
65 |
66 | @Test
67 | void verifyResultsPercentages() {
68 | assertTrue(allLineProtocolsContain(point, "serenity_results_percentages_success=92"));
69 | assertTrue(allLineProtocolsContain(point, "serenity_results_percentages_pending=8"));
70 | assertTrue(allLineProtocolsContain(point, "serenity_results_percentages_ignored=0"));
71 | }
72 |
73 | @Test
74 | void verifyResultsTimings() {
75 | assertTrue(allLineProtocolsContain(point, "serenity_results_max_test_duration=199957"));
76 | assertTrue(allLineProtocolsContain(point, "serenity_results_total_clock_duration=489836"));
77 | assertTrue(allLineProtocolsContain(point, "serenity_results_min_test_duration=1714"));
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/src/test/java/jenkinsci/plugins/influxdb/generators/MetricsPointGeneratorTest.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.generators;
2 |
3 | import hudson.model.Job;
4 | import hudson.model.Run;
5 | import hudson.model.TaskListener;
6 | import jenkins.metrics.impl.TimeInQueueAction;
7 | import jenkins.model.Jenkins;
8 | import jenkinsci.plugins.influxdb.models.AbstractPoint;
9 | import jenkinsci.plugins.influxdb.renderer.ProjectNameRenderer;
10 | import org.apache.commons.lang.StringUtils;
11 | import org.junit.jupiter.api.BeforeEach;
12 | import org.junit.jupiter.api.Test;
13 | import org.mockito.Mockito;
14 |
15 | import static org.junit.jupiter.api.Assertions.assertTrue;
16 |
17 | class MetricsPointGeneratorTest extends PointGeneratorBaseTest {
18 |
19 | private static final String JOB_NAME = "master";
20 | private static final int BUILD_NUMBER = 11;
21 | private static final String CUSTOM_PREFIX = "test_prefix";
22 |
23 | private Run build;
24 | private TaskListener listener;
25 | private ProjectNameRenderer measurementRenderer;
26 | private TimeInQueueAction timeInQueueAction;
27 |
28 | private long currTime;
29 |
30 | @BeforeEach
31 | void before() {
32 | build = Mockito.mock(Run.class);
33 | Job job = Mockito.mock(Job.class);
34 | listener = Mockito.mock(TaskListener.class);
35 | measurementRenderer = new ProjectNameRenderer(CUSTOM_PREFIX, null);
36 | timeInQueueAction = Mockito.mock(TimeInQueueAction.class);
37 |
38 | Mockito.when(build.getNumber()).thenReturn(BUILD_NUMBER);
39 | Mockito.when(build.getParent()).thenReturn(job);
40 | Mockito.when(job.getName()).thenReturn(JOB_NAME);
41 | Mockito.when(build.getAction(TimeInQueueAction.class)).thenReturn(timeInQueueAction);
42 | Mockito.when(job.getRelativeNameFrom(Mockito.nullable(Jenkins.class))).thenReturn("folder/" + JOB_NAME);
43 |
44 | currTime = System.currentTimeMillis();
45 | }
46 |
47 | @Test
48 | void measurement_successfully_generated() {
49 | Mockito.when(timeInQueueAction.getBlockedDurationMillis()).thenReturn((long) 10);
50 | Mockito.when(timeInQueueAction.getBuildableDurationMillis()).thenReturn((long) 20);
51 | Mockito.when(timeInQueueAction.getBuildingDurationMillis()).thenReturn((long) 30);
52 | Mockito.when(timeInQueueAction.getExecutingTimeMillis()).thenReturn((long) 40);
53 | Mockito.when(timeInQueueAction.getExecutorUtilization()).thenReturn(0.5);
54 | Mockito.when(timeInQueueAction.getQueuingDurationMillis()).thenReturn((long) 50);
55 | Mockito.when(timeInQueueAction.getSubTaskCount()).thenReturn(2);
56 | Mockito.when(timeInQueueAction.getTotalDurationMillis()).thenReturn((long) 60);
57 | Mockito.when(timeInQueueAction.getWaitingDurationMillis()).thenReturn((long) 70);
58 |
59 | MetricsPointGenerator generator = new MetricsPointGenerator(build, listener, measurementRenderer, currTime, StringUtils.EMPTY, CUSTOM_PREFIX);
60 | AbstractPoint[] points = generator.generate();
61 |
62 | assertTrue(allLineProtocolsContain(points[0], "blocked_time=10"));
63 | assertTrue(allLineProtocolsContain(points[0], "buildable_time=20"));
64 | assertTrue(allLineProtocolsContain(points[0], "building_time=30"));
65 | assertTrue(allLineProtocolsContain(points[0], "executing_time=40"));
66 | assertTrue(allLineProtocolsContain(points[0], "executor_utilization=0.5"));
67 | assertTrue(allLineProtocolsContain(points[0], "queue_time=50"));
68 | assertTrue(allLineProtocolsContain(points[0], "subtask_count=2"));
69 | assertTrue(allLineProtocolsContain(points[0], "total_duration=60"));
70 | assertTrue(allLineProtocolsContain(points[0], "waiting_time=70"));
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/src/main/java/jenkinsci/plugins/influxdb/models/AbstractPoint.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.models;
2 |
3 | import javax.annotation.Nonnull;
4 | import java.util.Map;
5 |
6 | public class AbstractPoint {
7 | private final com.influxdb.client.write.Point v1v2Point;
8 | private final com.influxdb.v3.client.Point v3Point;
9 |
10 | public AbstractPoint(@Nonnull String measurement) {
11 | this.v1v2Point = new com.influxdb.client.write.Point(measurement);
12 | this.v3Point = new com.influxdb.v3.client.Point(measurement);
13 | }
14 |
15 | public String getName() {
16 | String v1v2Name = this.v1v2Point.toLineProtocol().split(",")[0];
17 | String v3Name = this.v3Point.toLineProtocol().split(",")[0];
18 | if (!v1v2Name.equals(v3Name)) {
19 | throw new RuntimeException("V1V2 point name '%s' differs from V3 point name '%s'".formatted(v1v2Name, v3Name));
20 | }
21 | return v1v2Name;
22 | }
23 |
24 | public com.influxdb.client.write.Point getV1v2Point() {
25 | return v1v2Point;
26 | }
27 |
28 | public com.influxdb.v3.client.Point getV3Point() {
29 | return v3Point;
30 | }
31 |
32 | public AbstractPoint addField(String field, boolean value) {
33 | this.v1v2Point.addField(field, value);
34 | this.v3Point.setField(field, value);
35 | return this;
36 | }
37 |
38 | public AbstractPoint addField(String field, int value) {
39 | this.v1v2Point.addField(field, value);
40 | this.v3Point.setField(field, value);
41 | return this;
42 | }
43 |
44 | public AbstractPoint addField(String field, long value) {
45 | this.v1v2Point.addField(field, value);
46 | this.v3Point.setField(field, value);
47 | return this;
48 | }
49 |
50 | public AbstractPoint addField(String field, double value) {
51 | this.v1v2Point.addField(field, value);
52 | this.v3Point.setField(field, value);
53 | return this;
54 | }
55 |
56 | public AbstractPoint addField(String field, Number value) {
57 | this.v1v2Point.addField(field, value);
58 | this.v3Point.setField(field, value);
59 | return this;
60 | }
61 |
62 | public AbstractPoint addField(String field, String value) {
63 | this.v1v2Point.addField(field, value);
64 | this.v3Point.setField(field, value);
65 | return this;
66 | }
67 |
68 | public AbstractPoint addFields(Map fields) {
69 | this.v1v2Point.addFields(fields);
70 | this.v3Point.setFields(fields);
71 | return this;
72 | }
73 |
74 | public AbstractPoint addTag(String name, String value) {
75 | this.v1v2Point.addTag(name, value);
76 | this.v3Point.setTag(name, value);
77 | return this;
78 | }
79 |
80 | public AbstractPoint addTags(Map tags) {
81 | this.v1v2Point.addTags(tags);
82 | this.v3Point.setTags(tags);
83 | return this;
84 | }
85 |
86 | public AbstractPoint time(long time, com.influxdb.v3.client.write.WritePrecision precision) {
87 | com.influxdb.client.domain.WritePrecision v1v2WritePrecision =
88 | com.influxdb.client.domain.WritePrecision.valueOf(precision.name());
89 | this.v1v2Point.time(time, v1v2WritePrecision);
90 | this.v3Point.setTimestamp(time, precision);
91 | return this;
92 | }
93 |
94 | public AbstractPoint time(long time, com.influxdb.client.domain.WritePrecision precision) {
95 | com.influxdb.v3.client.write.WritePrecision v3WritePrecision =
96 | com.influxdb.v3.client.write.WritePrecision.valueOf(precision.name());
97 | this.v1v2Point.time(time, precision);
98 | this.v3Point.setTimestamp(time, v3WritePrecision);
99 | return this;
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/main/java/jenkinsci/plugins/influxdb/global/GlobalRunListener.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.global;
2 |
3 | import hudson.EnvVars;
4 | import hudson.Extension;
5 | import hudson.model.AbstractProject;
6 | import hudson.model.Job;
7 | import hudson.model.Run;
8 | import hudson.model.TaskListener;
9 | import hudson.model.listeners.RunListener;
10 | import jenkins.model.Jenkins;
11 | import jenkinsci.plugins.influxdb.InfluxDbPublicationService;
12 | import jenkinsci.plugins.influxdb.InfluxDbPublisher;
13 | import jenkinsci.plugins.influxdb.models.Target;
14 | import org.apache.commons.lang3.StringUtils;
15 |
16 | import javax.annotation.Nonnull;
17 | import java.io.IOException;
18 | import java.util.ArrayList;
19 | import java.util.List;
20 | import java.util.regex.Pattern;
21 |
22 | /**
23 | * Listens to all builds being completed and publishes their metrics to InfluxDB.
24 | */
25 | @Extension
26 | public class GlobalRunListener extends RunListener> {
27 |
28 | private static final String VARIABLE_PREFIX = "INFLUXDB_PLUGIN_";
29 |
30 | @Override
31 | public void onCompleted(Run, ?> build, @Nonnull TaskListener listener) {
32 | // Gets the full path of the build's project
33 | String path = build.getParent().getRelativeNameFrom(Jenkins.getInstanceOrNull());
34 | // Gets the list of targets from the configuration
35 | Jenkins jenkins = Jenkins.getInstanceOrNull();
36 | if (jenkins == null) {
37 | return;
38 | }
39 | List targets = jenkins.getDescriptorByType(InfluxDbPublisher.DescriptorImpl.class).getTargets();
40 | // Selects the targets eligible as global listeners and which match the build path
41 | List selectedTargets = new ArrayList<>();
42 | for (Target target : targets) {
43 | // Checks if the target matches the path to the project
44 | // Skip build if it already publishes information on this target
45 | if (isTargetMatchingPath(target, path) && !isPublicationInBuild(target, build)) {
46 | selectedTargets.add(target);
47 | }
48 | }
49 | // If some targets are selected
50 | if (!selectedTargets.isEmpty()) {
51 |
52 | EnvVars env;
53 | try {
54 | env = build.getEnvironment(listener);
55 | } catch (IOException | InterruptedException e) {
56 | env = new EnvVars();
57 | }
58 |
59 | // Creates the publication service
60 | InfluxDbPublicationService publicationService = new InfluxDbPublicationService(
61 | selectedTargets,
62 | env.get(VARIABLE_PREFIX + "CUSTOM_PROJECT_NAME"),
63 | env.get(VARIABLE_PREFIX + "CUSTOM_PREFIX"),
64 | null,
65 | null,
66 | null,
67 | null,
68 | System.currentTimeMillis() * 1000000,
69 | env.expand(env.get(VARIABLE_PREFIX + "CUSTOM_FIELDS")),
70 | env.expand(env.get(VARIABLE_PREFIX + "CUSTOM_TAGS")),
71 | "jenkins_data"
72 | );
73 |
74 | // Publication
75 | publicationService.perform(build, listener, env);
76 | }
77 | }
78 |
79 | private boolean isPublicationInBuild(Target target, Run, ?> build) {
80 | Job, ?> parent = build.getParent();
81 | if (parent instanceof AbstractProject) {
82 | InfluxDbPublisher publisher = (InfluxDbPublisher) ((AbstractProject) parent).getPublishersList().get(InfluxDbPublisher.class);
83 | if (publisher != null) {
84 | String buildTarget = publisher.getSelectedTarget();
85 | return buildTarget != null && StringUtils.equals(buildTarget, target.getDescription());
86 | }
87 | }
88 | return false;
89 | }
90 |
91 | private boolean isTargetMatchingPath(@Nonnull Target target, @Nonnull String path) {
92 | if (target.isGlobalListener()) {
93 | String pattern = target.getGlobalListenerFilter();
94 | return StringUtils.isBlank(pattern) || Pattern.matches(pattern, path);
95 | }
96 | return false;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/test/java/jenkinsci/plugins/influxdb/generators/CustomDataPointGeneratorTest.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.generators;
2 |
3 | import hudson.model.Job;
4 | import hudson.model.Run;
5 | import hudson.model.TaskListener;
6 | import jenkins.model.Jenkins;
7 | import jenkinsci.plugins.influxdb.models.AbstractPoint;
8 | import jenkinsci.plugins.influxdb.renderer.ProjectNameRenderer;
9 | import org.apache.commons.lang.StringUtils;
10 | import org.junit.jupiter.api.BeforeEach;
11 | import org.junit.jupiter.api.Test;
12 | import org.mockito.Mockito;
13 |
14 | import java.util.Collections;
15 | import java.util.HashMap;
16 | import java.util.Map;
17 |
18 | import static org.junit.jupiter.api.Assertions.assertFalse;
19 | import static org.junit.jupiter.api.Assertions.assertTrue;
20 |
21 | class CustomDataPointGeneratorTest extends PointGeneratorBaseTest {
22 |
23 | private static final String JOB_NAME = "master";
24 | private static final int BUILD_NUMBER = 11;
25 | private static final String CUSTOM_PREFIX = "test_prefix";
26 | private static final String MEASUREMENT_NAME = "jenkins_data";
27 |
28 | private Run, ?> build;
29 | private TaskListener listener;
30 |
31 | private ProjectNameRenderer measurementRenderer;
32 |
33 | private long currTime;
34 |
35 | @BeforeEach
36 | void before() {
37 | build = Mockito.mock(Run.class);
38 | Job job = Mockito.mock(Job.class);
39 | listener = Mockito.mock(TaskListener.class);
40 | measurementRenderer = new ProjectNameRenderer(CUSTOM_PREFIX, null);
41 |
42 | Mockito.when(build.getNumber()).thenReturn(BUILD_NUMBER);
43 | Mockito.doReturn(job).when(build).getParent();
44 | Mockito.when(job.getName()).thenReturn(JOB_NAME);
45 | Mockito.when(job.getRelativeNameFrom(Mockito.nullable(Jenkins.class))).thenReturn("folder/" + JOB_NAME);
46 |
47 | currTime = System.currentTimeMillis();
48 | }
49 |
50 | @Test
51 | void hasReport() {
52 | //check with customDataMap = null
53 | CustomDataPointGenerator cdGen1 = new CustomDataPointGenerator(build, listener, measurementRenderer, currTime, StringUtils.EMPTY, CUSTOM_PREFIX, null, null, MEASUREMENT_NAME);
54 | assertFalse(cdGen1.hasReport());
55 |
56 | //check with empty customDataMap
57 | CustomDataPointGenerator cdGen2 = new CustomDataPointGenerator(build, listener, measurementRenderer, currTime, StringUtils.EMPTY, CUSTOM_PREFIX, Collections.emptyMap(), null, MEASUREMENT_NAME);
58 | assertFalse(cdGen2.hasReport());
59 | }
60 |
61 | @Test
62 | void generate() {
63 | Map customData = new HashMap<>();
64 | customData.put("test1", 11);
65 | customData.put("test2", 22);
66 |
67 | Map customDataTags = new HashMap<>();
68 | customDataTags.put("tag1", "myTag");
69 | CustomDataPointGenerator cdGen = new CustomDataPointGenerator(build, listener, measurementRenderer, currTime, StringUtils.EMPTY, CUSTOM_PREFIX, customData, customDataTags, MEASUREMENT_NAME);
70 | AbstractPoint[] points = cdGen.generate();
71 |
72 | assertTrue(allLineProtocolsStartWith(points[0], "jenkins_custom_data,prefix=test_prefix,project_name=test_prefix_master,project_namespace=folder,project_path=folder/master,tag1=myTag build_number=11i,build_time="));
73 | assertTrue(allLineProtocolsContain(points[0], "jenkins_custom_data,prefix=test_prefix,project_name=test_prefix_master,project_namespace=folder,project_path=folder/master,tag1=myTag build_number=11i"));
74 | }
75 |
76 | @Test
77 | void custom_measurement_included() {
78 | String customMeasurement = "custom_measurement";
79 | Map customData = new HashMap<>();
80 | customData.put("test1", 11);
81 |
82 | Map customDataTags = new HashMap<>();
83 | customDataTags.put("tag1", "myTag");
84 |
85 | CustomDataPointGenerator cdGen = new CustomDataPointGenerator(build, listener, measurementRenderer, currTime, StringUtils.EMPTY, CUSTOM_PREFIX, customData, customDataTags, customMeasurement);
86 | AbstractPoint[] pointsToWrite = cdGen.generate();
87 |
88 | assertTrue(allLineProtocolsStartWith(pointsToWrite[0], "custom_" + customMeasurement));
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/main/java/jenkinsci/plugins/influxdb/generators/JUnitPointGenerator.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.generators;
2 |
3 | import hudson.EnvVars;
4 | import hudson.model.Run;
5 | import hudson.model.TaskListener;
6 | import hudson.tasks.junit.CaseResult;
7 | import hudson.tasks.test.AbstractTestResultAction;
8 | import jenkinsci.plugins.influxdb.models.AbstractPoint;
9 | import jenkinsci.plugins.influxdb.renderer.ProjectNameRenderer;
10 | import org.apache.commons.collections.iterators.ReverseListIterator;
11 | import org.apache.commons.lang.StringUtils;
12 |
13 | import java.util.ArrayList;
14 | import java.util.List;
15 |
16 | public class JUnitPointGenerator extends AbstractPointGenerator {
17 |
18 | private static final String JUNIT_SUITE_NAME = "suite_name";
19 | private static final String JUNIT_TEST_NAME = "test_name";
20 | private static final String JUNIT_TEST_CLASS_FULL_NAME = "test_class_full_name";
21 | private static final String JUNIT_PIPELINE_STEP = "pipeline_step";
22 | private static final String JUNIT_TEST_STATUS = "test_status";
23 | private static final String JUNIT_TEST_STATUS_ORDINAL = "test_status_ordinal";
24 | private static final String JUNIT_DURATION = "test_duration";
25 | private static final String JUNIT_COUNT = "test_count";
26 |
27 | private final String customPrefix;
28 | private final TaskListener listener;
29 |
30 | private final EnvVars env;
31 |
32 | public JUnitPointGenerator(Run, ?> build, TaskListener listener,
33 | ProjectNameRenderer projectNameRenderer,
34 | long timestamp, String jenkinsEnvParameterTag,
35 | String customPrefix, EnvVars env) {
36 | super(build, listener, projectNameRenderer, timestamp, jenkinsEnvParameterTag);
37 | this.customPrefix = customPrefix;
38 | this.listener = listener;
39 | this.env = env;
40 | }
41 |
42 | /**
43 | * @return true, if environment variable LOG_JUNIT_RESULTS is set to true and JUnit Reports exist
44 | */
45 | @Override
46 | public boolean hasReport() {
47 | return Boolean.parseBoolean(env.getOrDefault("LOG_JUNIT_RESULTS", "false")) && hasTestResults(build);
48 | }
49 |
50 | private boolean hasTestResults(Run, ?> build) {
51 | return build.getAction(AbstractTestResultAction.class) != null;
52 | }
53 |
54 | @Override
55 | public AbstractPoint[] generate() {
56 |
57 | List points = new ArrayList<>();
58 |
59 | // iterate each caseResult to get suiteName, testName and testStatus
60 | List allTestResults = getAllTestResults(build);
61 |
62 | for (CaseResult caseResult : allTestResults) {
63 | AbstractPoint point = buildPoint("junit_data", customPrefix, build)
64 | .addField(JUNIT_SUITE_NAME, caseResult.getSuiteResult().getName())
65 | .addField(JUNIT_TEST_NAME, caseResult.getName())
66 | .addField(JUNIT_TEST_CLASS_FULL_NAME, caseResult.getClassName())
67 | .addField(JUNIT_PIPELINE_STEP, getCaseResultEnclosingFlowNodeString(caseResult))
68 | .addField(JUNIT_TEST_STATUS, caseResult.getStatus().toString())
69 | .addField(JUNIT_TEST_STATUS_ORDINAL, caseResult.getStatus().ordinal())
70 | .addField(JUNIT_DURATION, caseResult.getDuration())
71 | .addField(JUNIT_COUNT, 1L);
72 | points.add(point);
73 | }
74 |
75 | return points.toArray(new AbstractPoint[0]);
76 | }
77 |
78 | private List getAllTestResults(Run, ?> build) {
79 | //get tests from build
80 | AbstractTestResultAction testResultAction = build.getAction(AbstractTestResultAction.class);
81 |
82 | // create a list that contains all tests
83 | List allTestResults = new ArrayList<>();
84 | allTestResults.addAll(testResultAction.getFailedTests());
85 | allTestResults.addAll(testResultAction.getSkippedTests());
86 | allTestResults.addAll(testResultAction.getPassedTests());
87 |
88 | return allTestResults;
89 | }
90 |
91 | private String getCaseResultEnclosingFlowNodeString(CaseResult caseResult) {
92 | if (!caseResult.getEnclosingFlowNodeNames().isEmpty()) {
93 | return StringUtils.join(new ReverseListIterator(caseResult.getEnclosingFlowNodeNames()), " / ");
94 | }
95 | return "";
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/main/java/jenkinsci/plugins/influxdb/generators/ChangeLogPointGenerator.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.generators;
2 |
3 | import hudson.model.AbstractBuild;
4 | import hudson.model.Run;
5 | import hudson.model.TaskListener;
6 | import hudson.scm.ChangeLogSet;
7 | import jenkinsci.plugins.influxdb.models.AbstractPoint;
8 | import jenkinsci.plugins.influxdb.renderer.ProjectNameRenderer;
9 | import org.jenkinsci.plugins.workflow.job.WorkflowRun;
10 |
11 | import java.util.Collection;
12 | import java.util.List;
13 |
14 | public class ChangeLogPointGenerator extends AbstractPointGenerator {
15 |
16 | private static final String BUILD_DISPLAY_NAME = "display_name";
17 |
18 | private final String customPrefix;
19 |
20 | private StringBuilder affectedPaths;
21 |
22 | private StringBuilder messages;
23 |
24 | private StringBuilder culprits;
25 |
26 | private int commitCount = 0;
27 |
28 | public ChangeLogPointGenerator(Run, ?> build, TaskListener listener,
29 | ProjectNameRenderer projectNameRenderer,
30 | long timestamp, String jenkinsEnvParameterTag,
31 | String customPrefix) {
32 | super(build, listener, projectNameRenderer, timestamp, jenkinsEnvParameterTag);
33 | this.customPrefix = customPrefix;
34 | this.affectedPaths = new StringBuilder();
35 | this.messages = new StringBuilder();
36 | this.culprits = new StringBuilder();
37 | }
38 |
39 | public boolean hasReport() {
40 | if (build instanceof AbstractBuild) { // freestyle job
41 | getChangeLogFromAbstractBuild(build);
42 | } else if (build instanceof WorkflowRun) { // pipeline
43 | getChangeLogFromPipeline(build);
44 | }
45 | return this.getCommitCount() > 0;
46 | }
47 |
48 | public AbstractPoint[] generate() {
49 | AbstractPoint point = buildPoint("changelog_data", customPrefix, build);
50 |
51 | point.addField(BUILD_DISPLAY_NAME, build.getDisplayName())
52 | .addField("commit_messages", this.getMessages())
53 | .addField("culprits", this.getCulprits())
54 | .addField("affected_paths", this.getAffectedPaths())
55 | .addField("commit_count", this.getCommitCount());
56 |
57 | return new AbstractPoint[]{point};
58 | }
59 |
60 | private void getChangeLogFromAbstractBuild(Run, ?> run) {
61 | AbstractBuild, ?> abstractBuild = (AbstractBuild, ?>) run;
62 | ChangeLogSet extends ChangeLogSet.Entry> changeset = abstractBuild.getChangeSet();
63 | addChangeLogData(changeset);
64 | }
65 |
66 | private void getChangeLogFromPipeline(Run, ?> run) {
67 | WorkflowRun workflowRun = (WorkflowRun) run;
68 | List> changeLogsSets = workflowRun.getChangeSets();
69 | for (ChangeLogSet extends ChangeLogSet.Entry> changeLogSet : changeLogsSets) {
70 | addChangeLogData(changeLogSet);
71 | }
72 | }
73 |
74 | private void addChangeLogData(ChangeLogSet extends ChangeLogSet.Entry> changeLogSet) {
75 | for (ChangeLogSet.Entry str : changeLogSet) {
76 | Collection extends ChangeLogSet.AffectedFile> affectedFiles = str.getAffectedFiles();
77 | for (ChangeLogSet.AffectedFile affectedFile : affectedFiles) {
78 | this.affectedPaths.append(affectedFile.getPath());
79 | this.affectedPaths.append(", ");
80 | }
81 | this.messages.append(str.getMsg());
82 | this.messages.append(", ");
83 |
84 | this.culprits.append(str.getAuthor().getFullName());
85 | this.culprits.append(", ");
86 |
87 | this.commitCount += 1;
88 | }
89 | }
90 |
91 | private String getMessages() {
92 | return this.messages.length() > 0 ? this.messages.substring(0, this.messages.length() - 2) : "";
93 | }
94 |
95 | private String getCulprits() {
96 | return this.culprits.length() > 0 ? this.culprits.substring(0, this.culprits.length() - 2) : "";
97 | }
98 |
99 | private String getAffectedPaths() {
100 | return this.affectedPaths.length() > 0 ? this.affectedPaths.substring(0, this.affectedPaths.length() - 2) : "";
101 | }
102 |
103 | private int getCommitCount() {
104 | return this.commitCount;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/main/java/jenkinsci/plugins/influxdb/generators/AbstractPointGenerator.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.generators;
2 |
3 | import com.influxdb.client.domain.WritePrecision;
4 | import hudson.EnvVars;
5 | import hudson.model.Run;
6 | import hudson.model.TaskListener;
7 | import jenkins.model.Jenkins;
8 | import jenkinsci.plugins.influxdb.models.AbstractPoint;
9 | import jenkinsci.plugins.influxdb.renderer.ProjectNameRenderer;
10 | import org.apache.commons.lang3.StringUtils;
11 | import org.apache.commons.text.StringSubstitutor;
12 |
13 | import java.io.IOException;
14 | import java.io.StringReader;
15 | import java.util.Map;
16 | import java.util.Objects;
17 | import java.util.Properties;
18 | import java.util.stream.Collectors;
19 |
20 | public abstract class AbstractPointGenerator implements PointGenerator {
21 |
22 | public static final String PROJECT_NAMESPACE = "project_namespace";
23 | public static final String PROJECT_NAME = "project_name";
24 | public static final String PROJECT_PATH = "project_path";
25 | public static final String INSTANCE = "instance";
26 | public static final String BUILD_NUMBER = "build_number";
27 | public static final String CUSTOM_PREFIX = "prefix";
28 |
29 | protected final long timestamp;
30 | protected final Run, ?> build;
31 | protected final TaskListener listener;
32 | private final ProjectNameRenderer projectNameRenderer;
33 | private final String jenkinsEnvParameterTag;
34 | private final WritePrecision precision = WritePrecision.NS;
35 |
36 | public AbstractPointGenerator(Run, ?> build, TaskListener listener, ProjectNameRenderer projectNameRenderer, long timestamp, String jenkinsEnvParameterTag) {
37 | this.build = build;
38 | this.listener = listener;
39 | this.projectNameRenderer = Objects.requireNonNull(projectNameRenderer);
40 | this.timestamp = timestamp;
41 | this.jenkinsEnvParameterTag = jenkinsEnvParameterTag;
42 | }
43 |
44 | public AbstractPoint buildPoint(String name, String customPrefix, Run, ?> build) {
45 | return buildPoint(name, customPrefix, build, timestamp);
46 | }
47 |
48 | @Override
49 | public AbstractPoint buildPoint(String name, String customPrefix, Run, ?> build, long timestamp) {
50 | Jenkins instance = Jenkins.getInstanceOrNull();
51 | String projectName = projectNameRenderer.render(build);
52 | String projectPath = build.getParent().getRelativeNameFrom(instance);
53 | AbstractPoint point = new AbstractPoint(name)
54 | .addField(BUILD_NUMBER, build.getNumber())
55 | .time(timestamp, precision);
56 |
57 | if (customPrefix != null && !customPrefix.isEmpty()) {
58 | point.addTag(CUSTOM_PREFIX, customPrefix);
59 | }
60 |
61 | point.addTag(PROJECT_NAME, projectName);
62 | point.addTag(PROJECT_PATH, projectPath);
63 | point.addTag(INSTANCE, instance != null ? instance.getRootUrl() : "");
64 | point.addTag(PROJECT_NAMESPACE, projectPath.split("/")[0]);
65 |
66 |
67 | if (StringUtils.isNotBlank(jenkinsEnvParameterTag)) {
68 | Properties tagProperties = parsePropertiesString(jenkinsEnvParameterTag);
69 | Map tagMap = resolveEnvParameterAndTransformToMap(tagProperties);
70 | point.addTags(tagMap);
71 | }
72 |
73 | return point;
74 | }
75 |
76 | protected Properties parsePropertiesString(String propertiesString) {
77 | Properties properties = new Properties();
78 | try {
79 | StringReader reader = new StringReader(propertiesString);
80 | properties.load(reader);
81 | } catch (IOException e) {
82 | e.printStackTrace();
83 | }
84 | return properties;
85 | }
86 |
87 | protected Map resolveEnvParameterAndTransformToMap(Properties properties) {
88 | return properties.entrySet().stream().collect(
89 | Collectors.toMap(
90 | e -> e.getKey().toString(),
91 | e -> {
92 | String value = e.getValue().toString();
93 | return containsEnvParameter(value) ? resolveEnvParameter(value) : value;
94 | }
95 | )
96 | );
97 | }
98 |
99 | private boolean containsEnvParameter(String value) {
100 | return StringUtils.length(value) > 3 && StringUtils.contains(value, "${");
101 | }
102 |
103 | private String resolveEnvParameter(String stringValue) {
104 | try {
105 | EnvVars envVars = build.getEnvironment(listener);
106 | return StringSubstitutor.replace(stringValue, envVars);
107 | } catch (IOException | InterruptedException e) {
108 | e.printStackTrace();
109 | }
110 | return stringValue;
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/test/java/jenkinsci/plugins/influxdb/generators/GitPointGeneratorTest.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.generators;
2 |
3 | import hudson.model.HealthReport;
4 | import hudson.model.Job;
5 | import hudson.model.Run;
6 | import hudson.model.TaskListener;
7 | import hudson.plugins.git.Branch;
8 | import hudson.plugins.git.Revision;
9 | import hudson.plugins.git.util.BuildData;
10 | import jenkins.model.Jenkins;
11 | import jenkinsci.plugins.influxdb.models.AbstractPoint;
12 | import jenkinsci.plugins.influxdb.renderer.ProjectNameRenderer;
13 | import org.apache.commons.lang3.StringUtils;
14 | import org.junit.jupiter.api.BeforeEach;
15 | import org.junit.jupiter.api.Test;
16 | import org.mockito.Mockito;
17 |
18 | import java.util.*;
19 |
20 | import static org.junit.jupiter.api.Assertions.assertTrue;
21 |
22 | /**
23 | * @author Mathieu Delrocq
24 | */
25 | class GitPointGeneratorTest extends PointGeneratorBaseTest {
26 |
27 | private static final String CUSTOM_PREFIX = "test_prefix";
28 | private static final String JOB_NAME = "job_name";
29 | private static final String GIT_REPOSITORY = "repository";
30 | private static final String GIT_REFERENCE = "reference";
31 | private static final String GIT_REVISION = "revision";
32 |
33 | private Run, ?> build;
34 | private List gitActions;
35 | private BuildData gitAction1;
36 | private BuildData gitAction2;
37 | private TaskListener listener;
38 | private long currTime;
39 | private ProjectNameRenderer measurementRenderer;
40 | private Revision revision1;
41 | private Revision revision2;
42 | private Branch branch1;
43 | private Branch branch2;
44 |
45 | @BeforeEach
46 | void before() {
47 | // Global Mocks
48 | listener = Mockito.mock(TaskListener.class);
49 | currTime = System.currentTimeMillis();
50 | measurementRenderer = new ProjectNameRenderer(CUSTOM_PREFIX, null);
51 | Job, ?> job = Mockito.mock(Job.class);
52 | Mockito.when(job.getName()).thenReturn(JOB_NAME);
53 | Mockito.when(job.getRelativeNameFrom(Mockito.nullable(Jenkins.class))).thenReturn("folder/" + JOB_NAME);
54 | Mockito.when(job.getBuildHealth()).thenReturn(new HealthReport());
55 |
56 | build = Mockito.mock(Run.class);
57 | Mockito.doReturn(job).when(build).getParent();
58 | gitAction1 = Mockito.mock(BuildData.class);
59 | gitAction2 = Mockito.mock(BuildData.class);
60 | gitActions = new ArrayList<>();
61 | gitActions.add(gitAction1);
62 | gitActions.add(gitAction2);
63 | branch1 = Mockito.mock(Branch.class);
64 | branch2 = Mockito.mock(Branch.class);
65 | revision1 = Mockito.mock(Revision.class);
66 | revision2 = Mockito.mock(Revision.class);
67 | Mockito.when(build.getActions(BuildData.class)).thenReturn(gitActions);
68 | Mockito.when(gitAction1.getLastBuiltRevision()).thenReturn(revision1);
69 | Mockito.when(gitAction2.getLastBuiltRevision()).thenReturn(revision2);
70 | List branches1 = Collections.singletonList(branch1);
71 | List branches2 = Collections.singletonList(branch2);
72 | Mockito.when(revision1.getBranches()).thenReturn(branches1);
73 | Mockito.when(revision2.getBranches()).thenReturn(branches2);
74 | Mockito.when(branch1.getName()).thenReturn(GIT_REFERENCE);
75 | Mockito.when(branch2.getName()).thenReturn(GIT_REFERENCE + "2");
76 | Mockito.when(revision1.getSha1String()).thenReturn(GIT_REVISION);
77 | Mockito.when(revision2.getSha1String()).thenReturn(GIT_REVISION + "2");
78 | Set remoteUrls1 = new HashSet<>();
79 | remoteUrls1.add(GIT_REPOSITORY);
80 | Set remoteUrls2 = new HashSet<>();
81 | remoteUrls2.add(GIT_REPOSITORY + "2");
82 | Mockito.when(gitAction1.getRemoteUrls()).thenReturn(remoteUrls1);
83 | Mockito.when(gitAction2.getRemoteUrls()).thenReturn(remoteUrls2);
84 | }
85 |
86 | @Test
87 | void test_with_datas() {
88 | GitPointGenerator gen = new GitPointGenerator(build, listener, measurementRenderer, currTime, StringUtils.EMPTY,
89 | CUSTOM_PREFIX);
90 | assertTrue(gen.hasReport());
91 | AbstractPoint[] points = gen.generate();
92 | assertTrue(points != null && points.length != 0);
93 | assertTrue(points[0].getV1v2Point().hasFields());
94 | assertTrue(allLineProtocolsContain(points[0], "git_repository=\"repository\""));
95 | assertTrue(allLineProtocolsContain(points[0], "git_revision=\"revision\""));
96 | assertTrue(allLineProtocolsContain(points[0], "git_reference=\"reference\""));
97 | assertTrue(allLineProtocolsContain(points[1], "git_repository=\"repository2\""));
98 | assertTrue(allLineProtocolsContain(points[1], "git_revision=\"revision2\""));
99 | assertTrue(allLineProtocolsContain(points[1], "git_reference=\"reference2\""));
100 | }
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/src/test/java/jenkinsci/plugins/influxdb/generators/CustomDataMapPointGeneratorTest.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.generators;
2 |
3 | import hudson.model.Job;
4 | import hudson.model.Run;
5 | import hudson.model.TaskListener;
6 | import jenkins.model.Jenkins;
7 | import jenkinsci.plugins.influxdb.models.AbstractPoint;
8 | import jenkinsci.plugins.influxdb.renderer.ProjectNameRenderer;
9 | import org.apache.commons.lang.StringUtils;
10 | import org.junit.jupiter.api.BeforeEach;
11 | import org.junit.jupiter.api.Test;
12 | import org.mockito.Mockito;
13 |
14 | import java.util.Collections;
15 | import java.util.HashMap;
16 | import java.util.Map;
17 | import java.util.TreeMap;
18 |
19 | import static org.junit.jupiter.api.Assertions.assertEquals;
20 | import static org.junit.jupiter.api.Assertions.assertFalse;
21 |
22 | class CustomDataMapPointGeneratorTest extends PointGeneratorBaseTest {
23 |
24 | private static final String JOB_NAME = "master";
25 | private static final int BUILD_NUMBER = 11;
26 | private static final String CUSTOM_PREFIX = "test_prefix";
27 |
28 | private Run, ?> build;
29 | private TaskListener listener;
30 |
31 | private ProjectNameRenderer measurementRenderer;
32 |
33 | private long currTime;
34 |
35 | @BeforeEach
36 | void before() {
37 | build = Mockito.mock(Run.class);
38 | Job job = Mockito.mock(Job.class);
39 | listener = Mockito.mock(TaskListener.class);
40 | measurementRenderer = new ProjectNameRenderer(CUSTOM_PREFIX, null);
41 |
42 | Mockito.when(build.getNumber()).thenReturn(BUILD_NUMBER);
43 | Mockito.doReturn(job).when(build).getParent();
44 | Mockito.when(job.getName()).thenReturn(JOB_NAME);
45 | Mockito.when(job.getRelativeNameFrom(Mockito.nullable(Jenkins.class))).thenReturn("folder/" + JOB_NAME);
46 |
47 | currTime = System.currentTimeMillis();
48 | }
49 |
50 | @Test
51 | void hasReport() {
52 | //check with customDataMap = null
53 |
54 | CustomDataMapPointGenerator cdmGen1 = new CustomDataMapPointGenerator(build, listener, measurementRenderer,
55 | currTime, StringUtils.EMPTY, CUSTOM_PREFIX, null, null);
56 | assertFalse(cdmGen1.hasReport());
57 |
58 | //check with empty customDataMap
59 | CustomDataMapPointGenerator cdmGen2 = new CustomDataMapPointGenerator(build, listener, measurementRenderer,
60 | currTime, StringUtils.EMPTY, CUSTOM_PREFIX, Collections.emptyMap(), Collections.emptyMap());
61 | assertFalse(cdmGen2.hasReport());
62 | }
63 |
64 | @Test
65 | void generate() throws NoSuchFieldException, IllegalAccessException {
66 | Map customData1 = new HashMap<>();
67 | customData1.put("test1", 11);
68 | customData1.put("test2", 22);
69 |
70 | Map customData2 = new HashMap<>();
71 | customData2.put("test3", 33);
72 | customData2.put("test4", 44);
73 |
74 | Map> customDataMap = new HashMap<>();
75 | customDataMap.put("series1", customData1);
76 | customDataMap.put("series2", customData2);
77 |
78 | Map> customDataMapTags = new HashMap<>();
79 | Map customTags = new HashMap<>();
80 | customTags.put("build_result", "SUCCESS");
81 | customDataMapTags.put("series1", customTags);
82 |
83 | CustomDataMapPointGenerator cdmGen = new CustomDataMapPointGenerator(build, listener, measurementRenderer,
84 | currTime, StringUtils.EMPTY, CUSTOM_PREFIX, customDataMap, customDataMapTags);
85 | AbstractPoint[] pointsToWrite = cdmGen.generate();
86 |
87 | assertEquals("series2", pointsToWrite[0].getName());
88 | assertEquals("series1", pointsToWrite[1].getName());
89 |
90 | AbstractPoint p1 = pointsToWrite[1];
91 | AbstractPoint p2 = pointsToWrite[0];
92 |
93 | TreeMap expectedFieldsP1 = new TreeMap<>(Map.of(
94 | "build_number", 11,
95 | "test1", 11,
96 | "test2", 22
97 | ));
98 | TreeMap expectedTagsP1 = new TreeMap<>(Map.of(
99 | "build_result", "SUCCESS",
100 | "instance", "",
101 | "prefix", "test_prefix",
102 | "project_name", "test_prefix_master",
103 | "project_namespace", "folder",
104 | "project_path", "folder/master"
105 | ));
106 | checkPointTagsAndFields(p1, expectedFieldsP1, expectedTagsP1);
107 |
108 | TreeMap expectedFieldsP2 = new TreeMap<>(Map.of(
109 | "build_number", 11,
110 | "test3", 33,
111 | "test4", 44
112 | ));
113 |
114 | TreeMap expectedTagsP2 = new TreeMap<>(Map.of(
115 | "instance", "",
116 | "prefix", "test_prefix",
117 | "project_name", "test_prefix_master",
118 | "project_namespace", "folder",
119 | "project_path", "folder/master"
120 | ));
121 | checkPointTagsAndFields(p2, expectedFieldsP2, expectedTagsP2);
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/test/java/jenkinsci/plugins/influxdb/generators/PerfPublisherPointGeneratorTest.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.generators;
2 |
3 | import hudson.model.Job;
4 | import hudson.model.Run;
5 | import hudson.model.TaskListener;
6 | import hudson.plugins.PerfPublisher.PerfPublisherBuildAction;
7 | import hudson.plugins.PerfPublisher.Report.Metric;
8 | import hudson.plugins.PerfPublisher.Report.Report;
9 | import hudson.plugins.PerfPublisher.Report.ReportContainer;
10 | import jenkins.model.Jenkins;
11 | import jenkinsci.plugins.influxdb.models.AbstractPoint;
12 | import jenkinsci.plugins.influxdb.renderer.ProjectNameRenderer;
13 | import org.apache.commons.lang.StringUtils;
14 | import org.junit.jupiter.api.BeforeEach;
15 | import org.junit.jupiter.api.Test;
16 | import org.mockito.Mockito;
17 | import org.mockito.stubbing.Answer;
18 |
19 | import java.util.HashMap;
20 | import java.util.Map;
21 | import java.util.TreeMap;
22 |
23 | import static org.junit.jupiter.api.Assertions.*;
24 |
25 | /**
26 | * @author Eugene Schava
27 | */
28 | class PerfPublisherPointGeneratorTest extends PointGeneratorBaseTest {
29 |
30 | private static final String JOB_NAME = "master";
31 | private static final int BUILD_NUMBER = 11;
32 | private static final String CUSTOM_PREFIX = "test_prefix";
33 |
34 | private Run, ?> build;
35 | private TaskListener listener;
36 | private ProjectNameRenderer measurementRenderer;
37 | private ReportContainer reports;
38 |
39 | private long currTime;
40 |
41 | @BeforeEach
42 | void before() {
43 | build = Mockito.mock(Run.class);
44 | Job job = Mockito.mock(Job.class);
45 | listener = Mockito.mock(TaskListener.class);
46 | measurementRenderer = new ProjectNameRenderer(CUSTOM_PREFIX, null);
47 | PerfPublisherBuildAction buildAction = Mockito.mock(PerfPublisherBuildAction.class);
48 | reports = new ReportContainer();
49 |
50 | Mockito.when(build.getNumber()).thenReturn(BUILD_NUMBER);
51 | Mockito.doReturn(job).when(build).getParent();
52 | Mockito.when(job.getName()).thenReturn(JOB_NAME);
53 | Mockito.when(job.getRelativeNameFrom(Mockito.nullable(Jenkins.class))).thenReturn("folder/" + JOB_NAME);
54 | Mockito.when(build.getAction(PerfPublisherBuildAction.class)).thenReturn(buildAction);
55 |
56 | Mockito.when(buildAction.getReport()).thenAnswer((Answer) invocationOnMock -> reports.getReports().isEmpty() ? null : reports.getReports().get(0));
57 | Mockito.when(buildAction.getReports()).thenReturn(reports);
58 |
59 | currTime = System.currentTimeMillis();
60 | }
61 |
62 | @Test
63 | void hasReport() {
64 | PerfPublisherPointGenerator generator = new PerfPublisherPointGenerator(build, listener, measurementRenderer, currTime, StringUtils.EMPTY, CUSTOM_PREFIX);
65 | assertFalse(generator.hasReport());
66 |
67 | reports.addReport(new Report());
68 | generator = new PerfPublisherPointGenerator(build, listener, measurementRenderer, currTime, StringUtils.EMPTY, CUSTOM_PREFIX);
69 | assertTrue(generator.hasReport());
70 | }
71 |
72 | @Test
73 | void generate() throws Exception {
74 | Report report = new Report();
75 |
76 | hudson.plugins.PerfPublisher.Report.Test test = new hudson.plugins.PerfPublisher.Report.Test();
77 | test.setName("test.txt");
78 | test.setExecuted(true);
79 |
80 | Map metrics = new HashMap<>();
81 | Metric metric1 = new Metric();
82 | metric1.setMeasure(50);
83 | metric1.setRelevant(true);
84 | metric1.setUnit("ms");
85 | metrics.put("metric1", metric1);
86 | test.setMetrics(metrics);
87 |
88 | report.addTest(test);
89 | reports.addReport(report);
90 | PerfPublisherPointGenerator generator = new PerfPublisherPointGenerator(build, listener, measurementRenderer, currTime, StringUtils.EMPTY, CUSTOM_PREFIX);
91 | AbstractPoint[] points = generator.generate();
92 |
93 | // Check point/table names
94 | assertEquals("perfpublisher_summary", points[0].getName());
95 | assertEquals("perfpublisher_metric", points[1].getName());
96 | assertEquals("perfpublisher_test", points[2].getName());
97 | assertEquals("perfpublisher_test_metric", points[3].getName());
98 |
99 | TreeMap p1Fields = getPointFields(points[0]);
100 | assertEquals(1L, p1Fields.get("number_of_executed_tests"));
101 |
102 | TreeMap p2Fields = getPointFields(points[1]);
103 | assertEquals(50.0, p2Fields.get("average"));
104 | assertEquals(50.0, p2Fields.get("best"));
105 | assertEquals(50.0, p2Fields.get("worst"));
106 | assertEquals("metric1", p2Fields.get("metric_name"));
107 | assertEquals(50.0, p2Fields.get("average"));
108 |
109 | TreeMap p3Fields = getPointFields(points[2]);
110 | assertEquals("test.txt", p3Fields.get("test_name"));
111 | assertEquals(true, p3Fields.get("executed"));
112 |
113 | TreeMap p4Fields = getPointFields(points[3]);
114 | assertEquals("test.txt", p4Fields.get("test_name"));
115 | assertEquals(true, p4Fields.get("relevant"));
116 | assertEquals("ms", p4Fields.get("unit"));
117 | assertEquals(50.0, p4Fields.get("value"));
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/main/java/jenkinsci/plugins/influxdb/generators/AgentPointGenerator.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb.generators;
2 |
3 | import hudson.model.AbstractBuild;
4 | import hudson.model.Node;
5 | import hudson.model.Run;
6 | import hudson.model.TaskListener;
7 | import hudson.model.labels.LabelAtom;
8 | import jenkinsci.plugins.influxdb.models.AbstractPoint;
9 | import jenkinsci.plugins.influxdb.renderer.ProjectNameRenderer;
10 | import org.apache.commons.collections.CollectionUtils;
11 | import org.jenkinsci.plugins.workflow.actions.WorkspaceAction;
12 | import org.jenkinsci.plugins.workflow.flow.FlowExecution;
13 | import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner;
14 | import org.jenkinsci.plugins.workflow.graph.FlowGraphWalker;
15 | import org.jenkinsci.plugins.workflow.graph.FlowNode;
16 |
17 | import java.util.*;
18 |
19 |
20 | /**
21 | * @author Mathieu Delrocq
22 | */
23 | public class AgentPointGenerator extends AbstractPointGenerator {
24 |
25 | protected static final String AGENT_NAME = "agent_name";
26 | protected static final String AGENT_LABEL = "agent_label";
27 | protected static final String UNIQUE_ID = "unique_id";
28 |
29 | private List> agentPoints;
30 | private String customPrefix;
31 |
32 | public AgentPointGenerator(Run, ?> build, TaskListener listener, ProjectNameRenderer projectNameRenderer,
33 | long timestamp, String jenkinsEnvParameterTag, String customPrefix) {
34 | super(build, listener, projectNameRenderer, timestamp, jenkinsEnvParameterTag);
35 | this.agentPoints = getAgentPoints(build);
36 | this.customPrefix = customPrefix;
37 | }
38 |
39 | @Override
40 | public boolean hasReport() {
41 | return CollectionUtils.isNotEmpty(agentPoints);
42 | }
43 |
44 | @Override
45 | public AbstractPoint[] generate() {
46 | List points = new ArrayList<>();
47 | Map.Entry agentPoint = null;
48 | for (int i = 0; i < agentPoints.size(); i++) {
49 | agentPoint = agentPoints.get(i);
50 | AbstractPoint point = buildPoint("agent_data", customPrefix, build)//
51 | .addTag(UNIQUE_ID, String.valueOf(i + 1))//
52 | .addField(AGENT_NAME, agentPoint.getKey())//
53 | .addField(AGENT_LABEL, agentPoint.getValue());
54 | points.add(point);
55 | }
56 | return points.toArray(new AbstractPoint[0]);
57 | }
58 |
59 | public String getFirstAgent() {
60 | return !CollectionUtils.isEmpty(agentPoints) ? agentPoints.get(0).getKey() : "";
61 | }
62 |
63 | /**
64 | * Retrieve agent(s) used by the build and return {@link AgentPoint}
65 | *
66 | * @param build
67 | * @return list of {@link AgentPoint}
68 | */
69 | private List> getAgentPoints(Run, ?> build) {
70 | if (build instanceof AbstractBuild) {
71 | return getAgentFromAbstractBuild((AbstractBuild, ?>) build);
72 | } else if (build instanceof FlowExecutionOwner.Executable) {
73 | return getAgentsFromPipeline((FlowExecutionOwner.Executable) build);
74 | }
75 | return new ArrayList<>();
76 | }
77 |
78 | /**
79 | * Retrieve agent(s) for traditional jobs
80 | *
81 | * @param build
82 | * @return list of {@link AgentPoint}
83 | */
84 | private List> getAgentFromAbstractBuild(AbstractBuild, ?> build) {
85 | List> agentPointsList = new ArrayList<>();
86 | Node node = build.getBuiltOn();
87 | if (node != null) {
88 | agentPointsList
89 | .add(new AbstractMap.SimpleEntry<>(node.getDisplayName(), node.getLabelString()));
90 | }
91 | return agentPointsList;
92 | }
93 |
94 | /**
95 | * Retrieve agent(s) for pipeline jobs
96 | *
97 | * @param build
98 | * @return list of {@link AgentPoint}
99 | */
100 | private List> getAgentsFromPipeline(FlowExecutionOwner.Executable build) {
101 | List> agentPointsList = new ArrayList<>();
102 | FlowExecutionOwner flowExecutionOwner = build.asFlowExecutionOwner();
103 | if (flowExecutionOwner != null) {
104 | FlowExecution flowExecution = flowExecutionOwner.getOrNull();
105 | if (flowExecution != null) {
106 | FlowGraphWalker graphWalker = new FlowGraphWalker(flowExecution);
107 | for (FlowNode flowNode : graphWalker) {
108 | WorkspaceAction workspaceAction = flowNode.getAction(WorkspaceAction.class);
109 | if (null != workspaceAction) {
110 | Set labels = workspaceAction.getLabels();
111 | StringJoiner labelString = new StringJoiner(", ");
112 | labelString.setEmptyValue("");
113 | for (LabelAtom label : labels) {
114 | labelString.add(label.getName());
115 | }
116 | String nodeName = workspaceAction.getNode();
117 | agentPointsList
118 | .add(new AbstractMap.SimpleEntry<>(nodeName, labelString.toString()));
119 | }
120 | }
121 | }
122 | }
123 | return agentPointsList;
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/src/test/java/jenkinsci/plugins/influxdb/IntegrationTest.java:
--------------------------------------------------------------------------------
1 | package jenkinsci.plugins.influxdb;
2 |
3 | import hudson.model.FreeStyleBuild;
4 | import hudson.model.FreeStyleProject;
5 | import hudson.model.Result;
6 | import hudson.tasks.Shell;
7 | import jenkinsci.plugins.influxdb.models.Target;
8 | import org.junit.jupiter.api.Test;
9 | import org.junit.jupiter.api.condition.DisabledOnOs;
10 | import org.junit.jupiter.api.condition.OS;
11 |
12 | import java.util.*;
13 |
14 | import static org.junit.jupiter.api.Assertions.assertEquals;
15 | import static org.junit.jupiter.api.Assertions.assertTrue;
16 |
17 | @DisabledOnOs(OS.WINDOWS) // Docker is not available on Jenkins CI windows build agents
18 | public class IntegrationTest extends IntegrationBaseTest {
19 | @Test
20 | public void testInfluxDBTargetsAreAvailable() {
21 | InfluxDbGlobalConfig globalConfig = InfluxDbGlobalConfig.getInstance();
22 | List targets = globalConfig.getTargets();
23 | assertEquals(3, targets.size());
24 | assertTrue(targets.get(0).isGlobalListener());
25 | assertTrue(targets.get(1).isGlobalListener());
26 | assertTrue(targets.get(2).isGlobalListener());
27 | assertEquals(testEnv.get("INFLUXDB_V1_URL"), targets.get(0).getUrl());
28 | assertEquals(testEnv.get("INFLUXDB_V2_URL"), targets.get(1).getUrl());
29 | assertEquals(testEnv.get("INFLUXDB_V3_URL"), targets.get(2).getUrl());
30 | }
31 |
32 | @Test
33 | public void testInfluxDBReporting() throws Exception {
34 | FreeStyleProject project = jenkinsRule.createFreeStyleProject("influxdb-test-job");
35 | project.getBuildersList().add(new Shell("echo 'Hello from Integration Tests!'"));
36 | FreeStyleBuild build = project.scheduleBuild2(0).get();
37 |
38 | jenkinsRule.assertBuildStatus(Result.SUCCESS, build);
39 | List