├── doc
├── images
│ ├── splunkins_metadata.png
│ ├── pipeline_filter_option.png
│ ├── pipeline_sendsplunkfile.png
│ ├── app_splunk_app_jenkins_build.png
│ ├── app_splunk_app_jenkins_node.png
│ ├── json_jenkins_sourcetype_old.png
│ ├── splunk_for_jenkins_post_job.png
│ ├── splunk_for_jenkins_config_basic.png
│ ├── app_splunk_app_jenkins_master_health.png
│ ├── app_splunk_app_jenkins_node_overview.png
│ └── app_splunk_app_jenkins_testanalysis.png
├── splunk-devops-extend-usage.md
└── extension.md
├── Jenkinsfile
├── splunk-devops
└── src
│ ├── test
│ ├── resources
│ │ ├── com
│ │ │ └── splunk
│ │ │ │ └── splunkjenkins
│ │ │ │ ├── CoverageMetricTest
│ │ │ │ ├── config.xml
│ │ │ │ └── jobs
│ │ │ │ │ ├── JaCoCo
│ │ │ │ │ ├── workspace
│ │ │ │ │ │ ├── jacoco.exec
│ │ │ │ │ │ ├── classes
│ │ │ │ │ │ │ └── com
│ │ │ │ │ │ │ │ └── mycompany
│ │ │ │ │ │ │ │ ├── App.class
│ │ │ │ │ │ │ │ └── App$Util.class
│ │ │ │ │ │ └── java
│ │ │ │ │ │ │ └── com
│ │ │ │ │ │ │ └── mycompany
│ │ │ │ │ │ │ └── App.java
│ │ │ │ │ └── config.xml
│ │ │ │ │ ├── Cobertura
│ │ │ │ │ ├── workspace
│ │ │ │ │ │ └── coverage.xml
│ │ │ │ │ └── config.xml
│ │ │ │ │ └── Clover
│ │ │ │ │ ├── config.xml
│ │ │ │ │ └── workspace
│ │ │ │ │ └── clover.xml
│ │ │ │ ├── PostBuildGroovyScriptTest.zip
│ │ │ │ └── TestResultAdapterTest
│ │ │ │ └── jobs
│ │ │ │ ├── testng_job1
│ │ │ │ └── config.xml
│ │ │ │ └── xunit_job1
│ │ │ │ └── config.xml
│ │ ├── splunk_metadata.properties
│ │ └── logging.properties
│ └── java
│ │ └── com
│ │ └── splunk
│ │ └── splunkjenkins
│ │ ├── SplunkResponse.java
│ │ ├── utils
│ │ ├── SystemConfigTest.java
│ │ ├── PlainTextConsoleUtilsTest.java
│ │ ├── LogEventHelperTest.java
│ │ └── TestCaseResultUtilsTest.java
│ │ ├── BaseTest.java
│ │ ├── JdkSplunkLogHandlerTest.java
│ │ ├── TestResultAdapterTest.java
│ │ ├── HealthMonitorTest.java
│ │ ├── SplunkSendJsonFileTest.java
│ │ ├── CoverageMetricTest.java
│ │ ├── listeners
│ │ └── LoggingQueueListenerTest.java
│ │ ├── TeeConsoleLogFilterTest.java
│ │ ├── SplunkArchiveFileTest.java
│ │ ├── SplunkLogServiceTest.java
│ │ └── SplunkJenkinsInstallationTest.java
│ └── main
│ ├── webapp
│ ├── images
│ │ ├── splunkIcon.png
│ │ ├── hec_token_list.png
│ │ ├── hec_global_config.png
│ │ ├── splunk_logo_black.png
│ │ └── splunk_logo_green.png
│ └── help-splunkAppUrl.html
│ ├── resources
│ ├── com
│ │ └── splunk
│ │ │ └── splunkjenkins
│ │ │ ├── SplunkJenkinsInstallation
│ │ │ ├── help-retriesOnError.html
│ │ │ ├── help-no-splunkAppUrl.html
│ │ │ ├── help-maxEventsBatchSize.html
│ │ │ ├── help-metadataHost.html
│ │ │ ├── help-metadataSource.html
│ │ │ ├── help-splunkAppUrl.html
│ │ │ ├── config.properties
│ │ │ ├── help-rawEventEnabled.html
│ │ │ ├── help-delay.html
│ │ │ ├── help-indexName.html
│ │ │ ├── help-globalPipelineFilter.html
│ │ │ ├── help-httpInputConfig.html
│ │ │ ├── lib.js
│ │ │ ├── help-host.html
│ │ │ ├── help-metaDataConfig.html
│ │ │ ├── help-token.html
│ │ │ ├── help-ignoredJobs.html
│ │ │ ├── help-groovyBinding.html
│ │ │ └── config.jelly
│ │ │ ├── SplunkArtifactNotifier
│ │ │ ├── help-skipGlobalSplunkArchive.html
│ │ │ ├── config.properties
│ │ │ ├── help-sizeLimit.html
│ │ │ ├── help-publishFromSlave.html
│ │ │ └── config.jelly
│ │ │ ├── listeners
│ │ │ └── Messages.properties
│ │ │ ├── model
│ │ │ └── MetaDataConfigItem
│ │ │ │ ├── help-value.html
│ │ │ │ ├── help-keyName.html
│ │ │ │ ├── help-dataSource.html
│ │ │ │ └── config.jelly
│ │ │ └── Messages.properties
│ ├── index.jelly
│ ├── sample.groovy
│ └── metadata.properties
│ ├── java
│ └── com
│ │ └── splunk
│ │ └── splunkjenkins
│ │ ├── model
│ │ ├── TestStatus.java
│ │ ├── EmptyTestCaseGroup.java
│ │ ├── JunitResultAggregateAdapter.java
│ │ ├── EventType.java
│ │ ├── LoggingJobExtractor.java
│ │ ├── JunitTestCaseGroup.java
│ │ ├── JunitResultAdapter.java
│ │ ├── AbstractTestResultAdapter.java
│ │ ├── TestCaseResult.java
│ │ └── CloverCoverageMetrics.java
│ │ ├── utils
│ │ ├── CoverageDetailJsonSerializer.java
│ │ ├── SpecialDoubleAdapter.java
│ │ ├── SpecialFloatAdapter.java
│ │ ├── MultipleHostResolver.java
│ │ ├── RemoteUtils.java
│ │ ├── CustomSSLConnectionSocketFactory.java
│ │ └── PlainTextConsoleUtils.java
│ │ ├── links
│ │ ├── HealthLinkAction.java
│ │ ├── ComputerLogActionFactory.java
│ │ ├── BuildableItemActionFactory.java
│ │ ├── LinkSplunkAction.java
│ │ ├── RunActionFactory.java
│ │ └── ReportAction.java
│ │ ├── listeners
│ │ ├── UserSecurityListener.java
│ │ ├── LoggingItemListener.java
│ │ ├── LoggingComputerListener.java
│ │ └── LoggingConfigListener.java
│ │ ├── LoggingInitStep.java
│ │ ├── WebPostAccessLogger.java
│ │ └── SplunkArtifactNotifier.java
│ └── groovy
│ └── com
│ └── splunk
│ └── splunkjenkins
│ └── UserActionDSL.groovy
├── splunk-devops-extend
└── src
│ ├── main
│ ├── resources
│ │ ├── com
│ │ │ └── splunk
│ │ │ │ └── splunkjenkins
│ │ │ │ ├── SplunkMessageStep
│ │ │ │ ├── help-globalScriptEnabled.html
│ │ │ │ └── config.jelly
│ │ │ │ ├── SplunkPipelineJobProperty
│ │ │ │ ├── help.html
│ │ │ │ ├── help-enableDiagram.html
│ │ │ │ └── config-details.jelly
│ │ │ │ ├── SplunkConsoleLogStep
│ │ │ │ └── config.jelly
│ │ │ │ └── SplunkLogFileStep
│ │ │ │ ├── config.properties
│ │ │ │ └── config.jelly
│ │ ├── index.jelly
│ │ └── logging.properties
│ └── java
│ │ └── com
│ │ └── splunk
│ │ └── splunkjenkins
│ │ ├── console
│ │ ├── DelayBufferedConsoleWork.java
│ │ ├── ConsoleRecordCacheUtils.java
│ │ ├── SplunkConsoleTaskListenerDecorator.java
│ │ ├── ConsoleNoteHandler.java
│ │ ├── LabelConsoleLineStream.java
│ │ ├── PipelineConsoleDecoder.java
│ │ └── SplunkTaskListenerFactory.java
│ │ ├── SplunkPipelineJobProperty.java
│ │ ├── SplunkinsDslVariable.java
│ │ ├── PipelineGraphVizSupport.java
│ │ ├── SplunkConsoleLogStep.java
│ │ └── SplunkLogFileStep.java
│ └── test
│ └── java
│ └── com
│ └── splunk
│ └── splunkjenkins
│ ├── console
│ ├── ConsoleNoteHandlerTest.java
│ └── LabelMarkupTextTest.java
│ ├── SplunkinsDslVariableTest.java
│ ├── SplunkLogFileStepTest.java
│ ├── PipelineExecuteDiagramTest.java
│ ├── StageStepNodesTest.java
│ └── SplunkConsoleLogStepTest.java
├── .gitignore
├── Jenkinsfile.mvn
├── readme.md
└── splunk-devops-shaded
└── pom.xml
/doc/images/splunkins_metadata.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/splunk-devops-plugin/HEAD/doc/images/splunkins_metadata.png
--------------------------------------------------------------------------------
/doc/images/pipeline_filter_option.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/splunk-devops-plugin/HEAD/doc/images/pipeline_filter_option.png
--------------------------------------------------------------------------------
/doc/images/pipeline_sendsplunkfile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/splunk-devops-plugin/HEAD/doc/images/pipeline_sendsplunkfile.png
--------------------------------------------------------------------------------
/doc/images/app_splunk_app_jenkins_build.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/splunk-devops-plugin/HEAD/doc/images/app_splunk_app_jenkins_build.png
--------------------------------------------------------------------------------
/doc/images/app_splunk_app_jenkins_node.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/splunk-devops-plugin/HEAD/doc/images/app_splunk_app_jenkins_node.png
--------------------------------------------------------------------------------
/doc/images/json_jenkins_sourcetype_old.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/splunk-devops-plugin/HEAD/doc/images/json_jenkins_sourcetype_old.png
--------------------------------------------------------------------------------
/doc/images/splunk_for_jenkins_post_job.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/splunk-devops-plugin/HEAD/doc/images/splunk_for_jenkins_post_job.png
--------------------------------------------------------------------------------
/Jenkinsfile:
--------------------------------------------------------------------------------
1 | // https://github.com/jenkins-infra/pipeline-library
2 | buildPlugin(platforms: ['linux'], jdkVersions: [17], useArtifactCachingProxy:false)
3 |
--------------------------------------------------------------------------------
/doc/images/splunk_for_jenkins_config_basic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/splunk-devops-plugin/HEAD/doc/images/splunk_for_jenkins_config_basic.png
--------------------------------------------------------------------------------
/splunk-devops/src/test/resources/com/splunk/splunkjenkins/CoverageMetricTest/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/doc/images/app_splunk_app_jenkins_master_health.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/splunk-devops-plugin/HEAD/doc/images/app_splunk_app_jenkins_master_health.png
--------------------------------------------------------------------------------
/doc/images/app_splunk_app_jenkins_node_overview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/splunk-devops-plugin/HEAD/doc/images/app_splunk_app_jenkins_node_overview.png
--------------------------------------------------------------------------------
/doc/images/app_splunk_app_jenkins_testanalysis.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/splunk-devops-plugin/HEAD/doc/images/app_splunk_app_jenkins_testanalysis.png
--------------------------------------------------------------------------------
/splunk-devops/src/main/webapp/images/splunkIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/splunk-devops-plugin/HEAD/splunk-devops/src/main/webapp/images/splunkIcon.png
--------------------------------------------------------------------------------
/splunk-devops/src/main/webapp/help-splunkAppUrl.html:
--------------------------------------------------------------------------------
1 |
Please configure the URL for splunk_app_jenkins in
2 | Splunk plugin configure advance section
3 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/webapp/images/hec_token_list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/splunk-devops-plugin/HEAD/splunk-devops/src/main/webapp/images/hec_token_list.png
--------------------------------------------------------------------------------
/splunk-devops/src/main/webapp/images/hec_global_config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/splunk-devops-plugin/HEAD/splunk-devops/src/main/webapp/images/hec_global_config.png
--------------------------------------------------------------------------------
/splunk-devops/src/main/webapp/images/splunk_logo_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/splunk-devops-plugin/HEAD/splunk-devops/src/main/webapp/images/splunk_logo_black.png
--------------------------------------------------------------------------------
/splunk-devops/src/main/webapp/images/splunk_logo_green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/splunk-devops-plugin/HEAD/splunk-devops/src/main/webapp/images/splunk_logo_green.png
--------------------------------------------------------------------------------
/splunk-devops-extend/src/main/resources/com/splunk/splunkjenkins/SplunkMessageStep/help-globalScriptEnabled.html:
--------------------------------------------------------------------------------
1 |
2 | Run the customized command in the plugin config page
3 |
--------------------------------------------------------------------------------
/splunk-devops-extend/src/main/resources/com/splunk/splunkjenkins/SplunkPipelineJobProperty/help.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Enable sending pipeline execution diagram by default
4 |
5 |
--------------------------------------------------------------------------------
/splunk-devops/src/test/resources/splunk_metadata.properties:
--------------------------------------------------------------------------------
1 | source=unit_test
2 | host=dev
3 | sourcetype=json:jenkins
4 | sourcetype_text=text:jenkins
5 | file.sourcetype=text:jenkins
6 | json_file.sourcetype=json:jenkins
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/SplunkJenkinsInstallation/help-retriesOnError.html:
--------------------------------------------------------------------------------
1 |
2 |
The number of times the plugin will try to send data to Splunk before giving up.
3 |
4 |
--------------------------------------------------------------------------------
/splunk-devops-extend/src/main/resources/com/splunk/splunkjenkins/SplunkConsoleLogStep/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 | Forwarding pipeline console log to Splunk
4 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/SplunkJenkinsInstallation/help-no-splunkAppUrl.html:
--------------------------------------------------------------------------------
1 | Please configure the URL for splunk_app_jenkins in
2 | Splunk plugin configure advance section
3 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/SplunkJenkinsInstallation/help-maxEventsBatchSize.html:
--------------------------------------------------------------------------------
1 |
2 |
The buffer size of the batch of events to send to Splunk. The default value is 262144(256KB).
3 |
4 |
--------------------------------------------------------------------------------
/splunk-devops/src/test/resources/com/splunk/splunkjenkins/PostBuildGroovyScriptTest.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/splunk-devops-plugin/HEAD/splunk-devops/src/test/resources/com/splunk/splunkjenkins/PostBuildGroovyScriptTest.zip
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/SplunkJenkinsInstallation/help-metadataHost.html:
--------------------------------------------------------------------------------
1 |
2 | Jenkins master hostname, used in Splunk query
3 |
4 | search host="servername"
5 |
6 |
--------------------------------------------------------------------------------
/splunk-devops-extend/src/main/resources/index.jelly:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | This plugin extracts pipeline job stages information
7 |
8 |
--------------------------------------------------------------------------------
/splunk-devops-extend/src/main/resources/com/splunk/splunkjenkins/SplunkPipelineJobProperty/help-enableDiagram.html:
--------------------------------------------------------------------------------
1 |
2 | pipeline execution diagram is described in graphviz dot format, see also
3 | http://graphviz.org/documentation/
4 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/SplunkJenkinsInstallation/help-metadataSource.html:
--------------------------------------------------------------------------------
1 |
2 | Event source name, where the event originated, used in Splunk query
3 |
4 | search source="sourcename"
5 |
6 |
--------------------------------------------------------------------------------
/splunk-devops-extend/src/main/resources/logging.properties:
--------------------------------------------------------------------------------
1 | handlers=java.util.logging.ConsoleHandler
2 | java.util.logging.ConsoleHandler.level=ALL
3 | com.splunk.splunkjenkins.console.level=FINEST
4 | java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
--------------------------------------------------------------------------------
/splunk-devops/src/test/resources/com/splunk/splunkjenkins/CoverageMetricTest/jobs/JaCoCo/workspace/jacoco.exec:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/splunk-devops-plugin/HEAD/splunk-devops/src/test/resources/com/splunk/splunkjenkins/CoverageMetricTest/jobs/JaCoCo/workspace/jacoco.exec
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/SplunkArtifactNotifier/help-skipGlobalSplunkArchive.html:
--------------------------------------------------------------------------------
1 |
2 | To skip the global post job archiving DSL e.g.
3 |
4 | archive("**/*.log")
5 |
6 | when the DSL does not fit for specific set of job.
7 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/SplunkJenkinsInstallation/help-splunkAppUrl.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/SplunkJenkinsInstallation/config.properties:
--------------------------------------------------------------------------------
1 | ConfigTitle=Splunk for Jenkins Configuration
2 | Hostname=Splunk Host Name
3 | default_metadata=\
4 | host={0}\
5 | #audit trail is not enabled by default\n\
6 | jenkins_config.enabled=false
7 | #end of metadata
--------------------------------------------------------------------------------
/splunk-devops/src/test/resources/com/splunk/splunkjenkins/CoverageMetricTest/jobs/JaCoCo/workspace/classes/com/mycompany/App.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/splunk-devops-plugin/HEAD/splunk-devops/src/test/resources/com/splunk/splunkjenkins/CoverageMetricTest/jobs/JaCoCo/workspace/classes/com/mycompany/App.class
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/SplunkJenkinsInstallation/help-rawEventEnabled.html:
--------------------------------------------------------------------------------
1 |
2 |
HTTP Event Collector supports arbitrary data formats similar to Splunk forwarders,
3 | relying on line-breaking rules etc. Supported in Splunk 6.3.1511 and later release
4 |
5 |
6 |
--------------------------------------------------------------------------------
/splunk-devops/src/test/resources/com/splunk/splunkjenkins/CoverageMetricTest/jobs/JaCoCo/workspace/classes/com/mycompany/App$Util.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jenkinsci/splunk-devops-plugin/HEAD/splunk-devops/src/test/resources/com/splunk/splunkjenkins/CoverageMetricTest/jobs/JaCoCo/workspace/classes/com/mycompany/App$Util.class
--------------------------------------------------------------------------------
/splunk-devops-extend/src/main/resources/com/splunk/splunkjenkins/SplunkPipelineJobProperty/config-details.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/index.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 | Splunk plugin for Jenkins provides deep insights into your Jenkins controller and agent infrastructure, job and
4 | build details such as console logs, status, artifacts, and an incredibly efficient way to analyze test results.
5 |
6 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/SplunkJenkinsInstallation/help-delay.html:
--------------------------------------------------------------------------------
1 |
2 |
Schedules the specified task for repeated fixed-rate execution beginning after the specified delay. Subsequent
3 | executions take place at approximately regular intervals, separated by the specified period.
4 |
5 |
--------------------------------------------------------------------------------
/splunk-devops/src/test/resources/logging.properties:
--------------------------------------------------------------------------------
1 | handlers=java.util.logging.FileHandler
2 | java.util.logging.FileHandler.pattern=debug.log
3 | java.util.logging.FileHandler.level=ALL
4 | com.splunk.splunkjenkins.level=FINEST
5 | org.apache.http.level = ALL
6 | jenkins.model.Jenkins.level = ALL
7 | java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/model/TestStatus.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.model;
2 |
3 | /**
4 | * TestNG used PASS,FAIL,SKIP, Junit(CaseResult.Status) used passed,skipped,pass,failed,fixed,regression
5 | * collapse them into 3 (passed, failure, skipped)
6 | */
7 | public enum TestStatus {
8 | PASSED, FAILURE, SKIPPED
9 | }
10 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/SplunkJenkinsInstallation/help-indexName.html:
--------------------------------------------------------------------------------
1 |
2 |
The index in Splunk to which the events will be indexed to. Defaults to "main" index in Splunk. Note: If using a
3 | custom index, that index must be already exist on your Splunk instance. Splunk-Jenkins will not create it for
4 | you.
5 |
6 |
--------------------------------------------------------------------------------
/splunk-devops-extend/src/main/resources/com/splunk/splunkjenkins/SplunkLogFileStep/config.properties:
--------------------------------------------------------------------------------
1 | ant_pattern=\
2 | You can use wildcards like "{1}" \
3 | See \
4 | the {0} attribute of Ant fileset for the exact format.\
5 | The base directory is the workspace .\
6 | You can only archive files that are located in your workspace.
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/SplunkArtifactNotifier/config.properties:
--------------------------------------------------------------------------------
1 | ant_pattern=\
2 | You can use wildcards like "{1}" \
3 | See \
4 | the {0} attribute of Ant fileset for the exact format.\
5 | The base directory is the workspace .\
6 | You can only archive files that are located in your workspace.
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/sample.groovy:
--------------------------------------------------------------------------------
1 | //send job metadata and junit reports with page size set to 50 (each event contains max 50 test cases)
2 | splunkins.sendTestReport(50)
3 | //send coverage, each event contains max 50 class metrics
4 | splunkins.sendCoverageReport(50)
5 | //send all logs from workspace to splunk, with each file size limits to 10MB
6 | splunkins.archive("**/*.log", null, false, "10MB")
7 |
8 | //end
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/SplunkArtifactNotifier/help-sizeLimit.html:
--------------------------------------------------------------------------------
1 |
2 | Limit the single file size to prevent publishing the whole huge log file generated accidentally by some program and uses up
3 | your Splunk daily volume license. The plugin will log a "file truncated to size:xx" event in Splunk when size limit is reached
4 | and will stop publishing the remaining content.
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 | work
3 |
4 | # Vim junk files
5 | .*.swp
6 | *~
7 |
8 | # IntelliJ project files
9 | .idea/
10 | *.iml
11 |
12 | # Eclipse IDE project files
13 | .classpath
14 | .project
15 | .settings/
16 | /splunkjenkins/nbproject/
17 |
18 | #debug log
19 | debug.log
20 | debug.log.lck
21 |
22 | # OS X
23 | .DS_Store
24 |
25 | # mvn versions:set
26 | pom.xml.versionsBackup
27 | pom.xml.releaseBackup
28 | dependency-reduced-pom.xml
29 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/SplunkJenkinsInstallation/help-globalPipelineFilter.html:
--------------------------------------------------------------------------------
1 |
2 | When enabled and
splunk-devops-extend is installed,
3 | all pipeline jobs' console logs will be sent by default.
4 | There is no need to use the
sendSplunkConsoleLog {…} step in Scripted Pipeline,
5 | or the
sendSplunkConsoleLog() option in Declarative Pipeline
6 |
7 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/listeners/Messages.properties:
--------------------------------------------------------------------------------
1 | audit.abort_job=aborted job {0}
2 | audit.start_job=started job {0}
3 | audit.create_item=created {0}
4 | audit.rename_item=renamed {0} to {1}
5 | audit.update_item=updated {0}
6 | audit.delete_item=deleted {0}
7 | audit.cloned_item=cloned item {0} from {1}
8 | audit.user_fail_login=failed to login
9 | audit.user_fail_auth=failed to authenticate
10 | audit.user_logout=logged out
11 | audit.user_login=logged in
--------------------------------------------------------------------------------
/splunk-devops-extend/src/main/resources/com/splunk/splunkjenkins/SplunkMessageStep/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/model/MetaDataConfigItem/help-value.html:
--------------------------------------------------------------------------------
1 |
2 | The event can be turned off by setting it to Disabled
3 | Index is a data repository in Splunk, you can set a different data retention policy and access privileges for each
4 | index in Splunk.
5 | Source Type is used by Splunk to determine how incoming data is formatted.
6 | It is recommended to use the plugin's default config if you are using Splunk App for Jenkins as the App expects a certain metadata format.
7 |
--------------------------------------------------------------------------------
/splunk-devops-extend/src/test/java/com/splunk/splunkjenkins/console/ConsoleNoteHandlerTest.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.console;
2 |
3 | import junit.framework.TestCase;
4 |
5 | public class ConsoleNoteHandlerTest extends TestCase {
6 |
7 | public void testRead() {
8 | String tag = " ";
9 | ConsoleNoteHandler handler = new ConsoleNoteHandler();
10 | handler.read(tag);
11 | assertEquals("test", handler.getLabel());
12 | assertEquals("testId", handler.getNodeId());
13 | }
14 | }
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/metadata.properties:
--------------------------------------------------------------------------------
1 | source=jenkins
2 | #for new type not specified with x.index=y
3 | index=jenkins_statistics
4 | sourcetype=json:jenkins
5 | sourcetype_text=text:jenkins
6 | #default index and source type settings
7 | build_report.index=jenkins
8 | build_event.index=jenkins_statistics
9 | queue_info.index=jenkins_statistics
10 | jenkins_config.index=jenkins_statistics
11 | slave_info.index=jenkins_statistics
12 | console_log.index=jenkins_console
13 | file.index=jenkins_artifact
14 | file.sourcetype=text:jenkins
15 | json_file.index=jenkins_artifact
16 | json_file.sourcetype=json:jenkins
--------------------------------------------------------------------------------
/splunk-devops/src/test/java/com/splunk/splunkjenkins/SplunkResponse.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins;
2 |
3 | import java.util.List;
4 | import java.util.Map;
5 |
6 | public class SplunkResponse {
7 | List entry;
8 |
9 | public String getFirst(String key) {
10 | return getItem(0, key);
11 | }
12 |
13 | public void setEntry(List entry) {
14 | this.entry = entry;
15 | }
16 |
17 | public String getItem(int idx, String key) {
18 | return entry.get(idx).content.get(key).toString();
19 | }
20 |
21 | public static class EntryItem {
22 | Map content;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/model/MetaDataConfigItem/help-keyName.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | The event can be turned off by setting it to Disabled.
4 |
5 | Index is a data repository in Splunk, you can set a different data retention policy and access privileges for
6 | each index in Splunk.
7 |
8 | Source Type is used by Splunk to determine how incoming data is formatted.
9 |
10 |
11 |
It is recommended to use the plugin's default config
12 | if you are using Splunk App for Jenkins as the App expects a certain metadata format.
13 |
14 |
15 |
--------------------------------------------------------------------------------
/splunk-devops/src/test/resources/com/splunk/splunkjenkins/CoverageMetricTest/jobs/JaCoCo/workspace/java/com/mycompany/App.java:
--------------------------------------------------------------------------------
1 | package com.mycompany;
2 |
3 | /**
4 | * Hello world!
5 | */
6 | public class App {
7 | public String getText() {
8 | return "hello";
9 | }
10 |
11 | public int getResult(boolean even) {
12 | if (even) {
13 | return 2;
14 | } else {
15 | return 1;
16 | }
17 | }
18 |
19 | public boolean isOk() {
20 | return false;
21 | }
22 |
23 | public static class Util {
24 | public static int calc(int a, int b) {
25 | return a + b;
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/SplunkJenkinsInstallation/help-httpInputConfig.html:
--------------------------------------------------------------------------------
1 |
2 |
You need enable HTTP Event Collector in Splunk.
3 | To enable it, go to Splunk Settings > Data inputs > HTTP Event Collector.
4 | Then click the Global Settings button in the upper-right corner.
5 | You can configure HTTP Port number and SSL on the Global Settings.
6 | Port is 8088, and SSL is enabled by default.
7 |
8 |
9 |
10 | See more details at HTTP Event Collector
11 | walk through
12 |
13 |
14 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/SplunkJenkinsInstallation/lib.js:
--------------------------------------------------------------------------------
1 | function checkDatasourceKey(event) {
2 | item=event.target
3 | var configItem = item.closest("[name=metadataItemSet]")
4 | if (configItem==null){
5 | return
6 | }
7 | var tableNode = configItem.querySelector(".splunk-meta-config-value")
8 | if (item.value == "disabled") {
9 | tableNode.style.display = "none"
10 | tableNode.nextSibling.style.display = "none"
11 | } else {
12 | tableNode.style.display = ""
13 | tableNode.nextSibling.style.display = "";
14 | }
15 | }
16 |
17 | Behaviour.specify(".splunk-meta-config-item", 'splk-config-item-check', 0, function (ele) {
18 | ele.onchange = checkDatasourceKey
19 | });
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/SplunkJenkinsInstallation/help-host.html:
--------------------------------------------------------------------------------
1 |
2 |
The Splunk Indexer hostname or IP address, multiple hosts separated by comma. Example:
3 | "192.168.1.4,192.168.1.5"
4 |
5 | For Splunk cloud user, the host name is something like http-inputs-xx.splunkcloud.com or
6 | http-inputs-xx.cloud.splunk.com, and HTTP Input Port is 443
7 |
8 | For Splunk enterprise user, the host name is the indexer host name, and HTTP Input Port is 8088 by default
9 |
10 |
11 | You need enable "HTTP Event Collector" In Splunk to use the plugin, please checkout
12 |
HTTP Event Collector
13 |
14 |
15 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/SplunkJenkinsInstallation/help-metaDataConfig.html:
--------------------------------------------------------------------------------
1 |
2 |
Specify index, host, source, soucetype for the events.
3 | index is a data repository in Splunk, please create the 4 indexes in Splunk:
4 |
5 | jenkins, jenkins_statistics, jenkins_console and jenkins_artifact
6 |
7 |
8 |
9 | host is used to identify which Jenkins master is the source of the data.
10 | the metadata can be used in Splunk query language such as
11 | index="jenkins" host="servername" sourcetype="json:jenkins"
12 |
13 |
14 |
15 | See more details
16 |
17 |
18 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/utils/CoverageDetailJsonSerializer.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.utils;
2 |
3 | import com.splunk.splunkjenkins.model.CoverageMetricsAdapter;
4 | import shaded.splk.com.google.gson.JsonElement;
5 | import shaded.splk.com.google.gson.JsonSerializationContext;
6 | import shaded.splk.com.google.gson.JsonSerializer;
7 |
8 | import java.lang.reflect.Type;
9 |
10 | public class CoverageDetailJsonSerializer implements JsonSerializer {
11 | @Override
12 | public JsonElement serialize(CoverageMetricsAdapter.CoverageDetail coverageDetail, Type type,
13 | JsonSerializationContext jsonSerializationContext) {
14 | return jsonSerializationContext.serialize(coverageDetail.getReport());
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/splunk-devops/src/test/java/com/splunk/splunkjenkins/utils/SystemConfigTest.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.utils;
2 |
3 | import com.splunk.splunkjenkins.listeners.LoggingConfigListener;
4 | import org.junit.Test;
5 |
6 | import static org.junit.Assert.assertFalse;
7 | import static org.junit.Assert.assertTrue;
8 |
9 | public class SystemConfigTest {
10 | @Test
11 | public void testConfigXmlIgnored() {
12 | assertTrue(LoggingConfigListener.IGNORED.matcher("/opt/jenkins/queue.xml").find());
13 | assertTrue(LoggingConfigListener.IGNORED.matcher("/opt/jenkins/hudson.model.UpdateCenter.xml").find());
14 | assertTrue(LoggingConfigListener.IGNORED.matcher("/opt/jenkins/job/foo/builds/1/config.xml").find());
15 | assertFalse(LoggingConfigListener.IGNORED.matcher("/opt/jenkins/job/foo/config.xml").find());
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/links/HealthLinkAction.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.links;
2 |
3 | import com.splunk.splunkjenkins.Messages;
4 | import com.splunk.splunkjenkins.SplunkJenkinsInstallation;
5 | import hudson.Extension;
6 | import hudson.model.ManagementLink;
7 |
8 | @SuppressWarnings("unused")
9 | @Extension
10 | public class HealthLinkAction extends ManagementLink {
11 | @Override
12 | public String getIconFileName() {
13 | return Messages.SplunkIconName();
14 | }
15 |
16 | @Override
17 | public String getDisplayName() {
18 | return "Jenkins Health";
19 | }
20 |
21 | @Override
22 | public String getUrlName() {
23 | SplunkJenkinsInstallation instance = SplunkJenkinsInstallation.get();
24 | return instance.getAppUrlOrHelp() + "jenkins_health?form.hostname=" + instance.getMetadataHost();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/SplunkJenkinsInstallation/help-token.html:
--------------------------------------------------------------------------------
1 |
2 |
You can find the HTTP Event Collector Token in Splunk, go to Splunk Settings > Data inputs > HTTP Event
3 | Collector.
4 |
5 |
6 |
7 | See more details at HTTP Event Collector
8 | walk through
9 |
10 |
To verify the token via curl command
11 |
12 | hec_host=<splunk-host>
13 | hec_port=8088
14 | hec_token=<splunk-token>
15 | curl -k "https://${hec_host}:${hec_port}/services/collector/event" -H "Authorization: Splunk ${hec_token}" -d \
16 | '{"host":"test-host","index":"jenkins_console","sourcetype":"json:jenkins","source":"logger://dummy","event":{"level":"INFO","log_source":"cmdline","message":"Test HEC"}}'
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/SplunkArtifactNotifier/help-publishFromSlave.html:
--------------------------------------------------------------------------------
1 |
2 | Publish log files directly from the agent, instead of proxy for the process to the master.
3 | When publish from agent is selected, Jenkins master will transfer the plugin and its dependence to agent and
4 | initiate
5 | the publishing process from the agent.
6 | Take below into consideration:
7 |
8 | agent and master load
9 |
10 |
11 | log file size
12 |
13 |
14 | agent type, long lived agent or one time use agent
15 |
16 |
17 | Rule of thumb for selecting publish type:
18 |
19 |
20 | a. if log files size is less than 5MB, publish from master is preferred.
21 | b. if Splunk instance is in an isolated network which is not reachable from agent, you need publish from
22 | master.
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/splunk-devops/src/test/java/com/splunk/splunkjenkins/BaseTest.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins;
2 |
3 | import com.splunk.splunkjenkins.utils.SplunkLogService;
4 | import org.junit.After;
5 | import org.junit.Before;
6 | import org.junit.Rule;
7 | import org.jvnet.hudson.test.JenkinsRule;
8 |
9 | import static com.splunk.splunkjenkins.SplunkConfigUtil.checkTokenAvailable;
10 | import static com.splunk.splunkjenkins.utils.LogEventHelper.getDefaultDslScript;
11 |
12 | public class BaseTest {
13 | @Rule
14 | public JenkinsRule j = new JenkinsRule();
15 |
16 | @Before
17 | public void setUp() throws Exception {
18 | org.junit.Assume.assumeTrue(checkTokenAvailable());
19 | SplunkJenkinsInstallation.get().setScriptContent(getDefaultDslScript());
20 | SplunkJenkinsInstallation.get().updateCache();
21 | }
22 |
23 | @After
24 | public void tearDown() {
25 | SplunkLogService.getInstance().stopWorker();
26 | SplunkLogService.getInstance().releaseConnection();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/utils/SpecialDoubleAdapter.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.utils;
2 |
3 |
4 | import shaded.splk.com.google.gson.TypeAdapter;
5 | import shaded.splk.com.google.gson.stream.JsonReader;
6 | import shaded.splk.com.google.gson.stream.JsonToken;
7 | import shaded.splk.com.google.gson.stream.JsonWriter;
8 |
9 | import java.io.IOException;
10 |
11 | public class SpecialDoubleAdapter extends TypeAdapter {
12 | @Override
13 | public void write(JsonWriter jsonWriter, Double number) throws IOException {
14 | if (number == null || Double.isNaN(number) || Double.isInfinite(number)) {
15 | jsonWriter.nullValue();
16 | } else {
17 | jsonWriter.value(number);
18 | }
19 | }
20 |
21 | @Override
22 | public Double read(JsonReader in) throws IOException {
23 | if (in.peek() == JsonToken.NULL) {
24 | in.nextNull();
25 | return null;
26 | }
27 | return in.nextDouble();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/utils/SpecialFloatAdapter.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.utils;
2 |
3 |
4 | import shaded.splk.com.google.gson.TypeAdapter;
5 | import shaded.splk.com.google.gson.stream.JsonReader;
6 | import shaded.splk.com.google.gson.stream.JsonToken;
7 | import shaded.splk.com.google.gson.stream.JsonWriter;
8 |
9 | import java.io.IOException;
10 |
11 | public class SpecialFloatAdapter extends TypeAdapter {
12 | @Override
13 | public void write(JsonWriter jsonWriter, Float number) throws IOException {
14 | if (number == null || Float.isNaN(number) || Float.isInfinite(number)) {
15 | jsonWriter.nullValue();
16 | } else {
17 | jsonWriter.value(number);
18 | }
19 | }
20 |
21 | @Override
22 | public Float read(JsonReader in) throws IOException {
23 | if (in.peek() == JsonToken.NULL) {
24 | in.nextNull();
25 | return null;
26 | }
27 | return (float) in.nextDouble();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/links/ComputerLogActionFactory.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.links;
2 |
3 | import com.splunk.splunkjenkins.SplunkJenkinsInstallation;
4 | import com.splunk.splunkjenkins.utils.LogEventHelper;
5 | import hudson.Extension;
6 | import hudson.model.Action;
7 | import hudson.model.Computer;
8 | import hudson.model.TransientComputerActionFactory;
9 |
10 | import java.util.Collections;
11 | import java.util.Collection;
12 |
13 | //TODO add agent page and update link
14 | @Extension
15 | public class ComputerLogActionFactory extends TransientComputerActionFactory {
16 | @Override
17 | public Collection extends Action> createFor(Computer target) {
18 | String query = new LogEventHelper.UrlQueryBuilder()
19 | .putIfAbsent("master", SplunkJenkinsInstallation.get().getMetadataHost())
20 | .putIfAbsent("slave", target.getName())
21 | .build();
22 | return Collections.singleton(new LinkSplunkAction("jenkins_slave", query, "Splunk"));
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/links/BuildableItemActionFactory.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.links;
2 |
3 | import com.splunk.splunkjenkins.SplunkJenkinsInstallation;
4 | import com.splunk.splunkjenkins.utils.LogEventHelper;
5 | import hudson.Extension;
6 | import hudson.Util;
7 | import hudson.model.AbstractProject;
8 | import hudson.model.Action;
9 | import hudson.model.BuildableItem;
10 | import jenkins.model.TransientActionFactory;
11 |
12 | import edu.umd.cs.findbugs.annotations.NonNull;
13 | import java.util.Collection;
14 | import java.util.Collections;
15 |
16 | @SuppressWarnings("unused")
17 | @Extension
18 | public class BuildableItemActionFactory extends TransientActionFactory {
19 | @Override
20 | public Class type() {
21 | return BuildableItem.class;
22 | }
23 |
24 | @NonNull
25 | @Override
26 | public Collection extends Action> createFor(@NonNull BuildableItem target) {
27 | return Collections.singleton(new LinkSplunkAction("build", "", "Splunk"));
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/splunk-devops-extend/src/main/resources/com/splunk/splunkjenkins/SplunkLogFileStep/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
10 |
11 |
12 |
14 |
15 |
16 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/utils/MultipleHostResolver.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.utils;
2 |
3 | import shaded.splk.org.apache.http.conn.DnsResolver;
4 |
5 | import java.net.InetAddress;
6 | import java.net.UnknownHostException;
7 | import java.util.ArrayList;
8 | import java.util.List;
9 |
10 | public class MultipleHostResolver implements DnsResolver {
11 | public static final String NAME_DELIMITER = ",";
12 |
13 | @Override
14 | public InetAddress[] resolve(final String host) throws UnknownHostException {
15 | if (host == null) {
16 | return null;
17 | }
18 | String hostname = host;
19 | //split by comma
20 | String[] hosts = hostname.split(NAME_DELIMITER);
21 | List addressList = new ArrayList<>();
22 | for (String endpointHost : hosts) {
23 | InetAddress[] addresses = InetAddress.getAllByName(endpointHost);
24 | for (InetAddress address : addresses) {
25 | addressList.add(address);
26 | }
27 | }
28 | return addressList.toArray(new InetAddress[0]);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/splunk-devops-extend/src/main/java/com/splunk/splunkjenkins/console/DelayBufferedConsoleWork.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.console;
2 |
3 | import hudson.Extension;
4 | import hudson.model.AsyncPeriodicWork;
5 | import hudson.model.TaskListener;
6 |
7 | import java.io.IOException;
8 | import java.util.concurrent.TimeUnit;
9 | import java.util.logging.Level;
10 |
11 | @Extension
12 | public class DelayBufferedConsoleWork extends AsyncPeriodicWork {
13 | // not shorter than 15s, default to 1 min 30s
14 | private long period = TimeUnit.SECONDS.toMillis(Math.max(15, Long.getLong(DelayBufferedConsoleWork.class.getName(), 90)));
15 |
16 | public DelayBufferedConsoleWork() {
17 | super("Flush cached splunk console log");
18 | }
19 |
20 | @Override
21 | protected void execute(TaskListener taskListener) throws IOException, InterruptedException {
22 | ConsoleRecordCacheUtils.flushLog();
23 | }
24 |
25 | @Override
26 | public long getRecurrencePeriod() {
27 | return period;
28 | }
29 |
30 | @Override
31 | protected Level getNormalLoggingLevel() {
32 | return Level.FINE;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/SplunkArtifactNotifier/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/SplunkJenkinsInstallation/help-ignoredJobs.html:
--------------------------------------------------------------------------------
1 |
2 | Check Regular
3 | Expression to select which build will be ignored for monitoring. Build url matches
4 | the pattern will be ignored, for example:
5 | (?:backgroundJobName|adhocJobName|tempJobName)
6 | will match backgroundJobName, backgroundJobNameBlah, blahbackgroundJobName
7 | ^(?:backgroundJobName|adhocJobName|tempJobName)$
8 | will match backgroundJobName but neither backgroundJobNameBlah nor blahbackgroundJobName
9 |
10 |
11 | (?:X)
12 | X, as a non-capturing group
13 |
14 |
15 | ^
16 | The beginning of a line
17 |
18 |
19 | $
20 | The end of a line
21 |
22 |
23 | X|Y
24 | Either X or Y
25 |
26 |
27 | \w
28 | A word character: [a-zA-Z_0-9]
29 |
30 |
31 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/model/EmptyTestCaseGroup.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.model;
2 |
3 | import hudson.tasks.test.TestResult;
4 |
5 | import java.util.Collections;
6 | import java.util.List;
7 |
8 | import static com.splunk.splunkjenkins.Constants.NO_TEST_REPORT_FOUND;
9 |
10 | public class EmptyTestCaseGroup extends JunitTestCaseGroup {
11 | private String message;
12 |
13 | public String getMessage() {
14 | return message;
15 | }
16 |
17 | @Override
18 | public int getFailures() {
19 | return 0;
20 | }
21 |
22 | @Override
23 | public int getPasses() {
24 | return 0;
25 | }
26 |
27 | @Override
28 | public int getSkips() {
29 | return 0;
30 | }
31 |
32 | @Override
33 | public int getTotal() {
34 | return 0;
35 | }
36 |
37 | @Override
38 | public float getDuration() {
39 | return 0;
40 | }
41 |
42 | @Override
43 | public List getTestcase() {
44 | return Collections.emptyList();
45 | }
46 |
47 | public void setWarning(boolean flag) {
48 | this.message = flag?NO_TEST_REPORT_FOUND:null;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/doc/splunk-devops-extend-usage.md:
--------------------------------------------------------------------------------
1 | ## A component of Splunk Plugin for Jenkins to support pipeline jobs
2 |
3 | ### Provided steps
4 | - sendSplunkFile
5 | ```Groovy
6 | sendSplunkFile includes: "*.log", sizeLimit: "50MB"
7 | ```
8 |
9 | 
10 |
11 | - sendSplunkConsoleLog
12 |
13 | since version 1.9.0, the plugin is able to capture all pipeline log via [TaskListenerDecorator](https://javadoc.jenkins.io/plugin/workflow-api/org/jenkinsci/plugins/workflow/log/TaskListenerDecorator.html)
14 |
15 | 
16 |
17 | sendSplunkConsoleLog step is only required when the global option is not checked
18 |
19 | ```Groovy
20 | // scripted pipeline
21 | sendSplunkConsoleLog {
22 | node{
23 | sh "echo testjob";
24 | }
25 | }
26 |
27 | // declarative pipeline
28 | pipeline {
29 | agent any
30 | options {
31 | timeout(time: 1, unit: 'HOURS')
32 | sendSplunkConsoleLog()
33 | }
34 | stages {
35 | stage('Example') {
36 | steps {
37 | echo 'Hello World'
38 | }
39 | }
40 | }
41 | }
42 | ```
43 |
44 |
45 | ## Change Log
46 | Located in the [CHANGELOG.md](CHANGELOG.md)
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/model/MetaDataConfigItem/help-dataSource.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Build Report
4 | Junit or other Test Reports sent by calling "send(message)" DSL script
5 |
6 |
7 |
8 | Build Event
9 | Sent when job are started and completed
10 |
11 |
12 |
13 | Queue Information
14 | Basic queue information and Jenkins health metrics
15 |
16 |
17 |
18 | Console Log
19 | Build console log, agent log and Jenkins master log (jenkins.log)
20 |
21 |
22 |
23 | Log File
24 | Artifact contents, send by calling "archive(includes, excludes)" DSL script
25 |
26 |
27 |
28 | Agent Information
29 | The agent monitor data
30 |
31 |
32 |
33 | Jenkins Config
34 | The contents of Jenkins configuration items, e.g. config.xml, sent when the config file get updated.
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/model/JunitResultAggregateAdapter.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.model;
2 |
3 | import hudson.Extension;
4 | import hudson.tasks.junit.CaseResult;
5 | import hudson.tasks.junit.SuiteResult;
6 | import hudson.tasks.test.AggregatedTestResultAction;
7 |
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | @Extension(optional = true)
12 | public class JunitResultAggregateAdapter extends AbstractTestResultAdapter {
13 | @Override
14 | public List getTestResult(AggregatedTestResultAction resultAction) {
15 | List caseResults = new ArrayList<>();
16 | for (AggregatedTestResultAction.ChildReport childReport : resultAction.getChildReports()) {
17 | if (childReport.result instanceof hudson.tasks.junit.TestResult) {
18 | hudson.tasks.junit.TestResult testResult = (hudson.tasks.junit.TestResult) childReport.result;
19 | for (SuiteResult suite : testResult.getSuites()) {
20 | for (CaseResult testCase : suite.getCases()) {
21 | caseResults.add(testCase);
22 | }
23 | }
24 | }
25 | }
26 | return caseResults;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/utils/RemoteUtils.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.utils;
2 |
3 | import com.splunk.splunkjenkins.SplunkJenkinsInstallation;
4 | import hudson.util.Secret;
5 | import org.apache.commons.beanutils.BeanUtils;
6 |
7 | import java.util.Map;
8 |
9 | public class RemoteUtils {
10 |
11 | public static void initSplunkConfigOnAgent(Map eventCollectorProperty) {
12 | // Init SplunkJenkins global config in slave, can not reference Jenkins.getInstance(), Xtream
13 | SplunkJenkinsInstallation config = new SplunkJenkinsInstallation(false);
14 | try {
15 | String tokenValue = (String) eventCollectorProperty.remove("token");
16 | BeanUtils.populate(config, eventCollectorProperty);
17 | config.setToken(Secret.fromString(tokenValue));
18 | config.setEnabled(true);
19 | initSplunkConfigOnAgent(config);
20 | } catch (Exception e) {
21 | e.printStackTrace();
22 | }
23 |
24 | }
25 |
26 | public static void initSplunkConfigOnAgent(SplunkJenkinsInstallation instance) {
27 | SplunkJenkinsInstallation.initOnAgent(instance);
28 | // only use one thread on agent
29 | SplunkLogService.getInstance().MAX_WORKER_COUNT = 1;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/model/EventType.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.model;
2 |
3 | public enum EventType {
4 | BUILD_REPORT(false),
5 | BUILD_EVENT(false),
6 | QUEUE_INFO(false),
7 | JENKINS_CONFIG(false),
8 | CONSOLE_LOG(true),
9 | FILE(true),
10 | SLAVE_INFO(false),
11 | LOG(false),
12 | BATCH_JSON(false),
13 | JSON_FILE(true);
14 |
15 | /**
16 | * whether the data need to be split by line breaker before send
17 | */
18 | private boolean needSplit;
19 |
20 | EventType(boolean needSplit) {
21 | this.needSplit = needSplit;
22 | }
23 |
24 | /**
25 | * Need spit the content line by line if raw event not supported
26 | * Only applied for non-structural data, such as file and console text.
27 | * It doesn't applied for json data or xml data
28 | *
29 | * @return true if need spit the contents line by line if raw event not supported;
30 | * false otherwise.
31 | */
32 | public boolean needSplit() {
33 | return needSplit;
34 | }
35 |
36 | /**
37 | * @param suffix the config metadata, can be either index, source or sourcetype
38 | * @return return name.suffix
39 | */
40 | public String getKey(String suffix) {
41 | return this.name().toLowerCase() + "." + suffix;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/splunk-devops/src/test/java/com/splunk/splunkjenkins/JdkSplunkLogHandlerTest.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins;
2 |
3 | import org.junit.Before;
4 | import org.junit.Test;
5 |
6 | import java.util.UUID;
7 | import java.util.logging.Handler;
8 | import java.util.logging.Logger;
9 |
10 | import static com.splunk.splunkjenkins.SplunkConfigUtil.verifySplunkSearchResult;
11 | import static org.junit.Assert.assertTrue;
12 |
13 | public class JdkSplunkLogHandlerTest extends BaseTest {
14 |
15 | @Before
16 | public void setUp() throws Exception {
17 | super.setUp();
18 | LoggingInitStep.registerHandler();
19 | }
20 |
21 | @Test
22 | public void publish() throws Exception {
23 | Handler[] handlers = Logger.getLogger("").getHandlers();
24 | boolean found = false;
25 | for (Handler handler : handlers) {
26 | if (handler instanceof JdkSplunkLogHandler) {
27 | found = true;
28 | break;
29 | }
30 | }
31 | assertTrue("LoggingInitStep.setupSplunkJenkins() should be called", found);
32 | String message = "test_log_" + UUID.randomUUID();
33 | Logger.getLogger("test_info_logger").info(message);
34 | Logger.getLogger("test_warning_logger").warning(message);
35 | verifySplunkSearchResult(message + " level=WARNING", 1);
36 | verifySplunkSearchResult(message + " level=INFO", 1);
37 | }
38 |
39 | }
--------------------------------------------------------------------------------
/splunk-devops/src/test/java/com/splunk/splunkjenkins/TestResultAdapterTest.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins;
2 |
3 | import hudson.model.FreeStyleProject;
4 | import hudson.model.Run;
5 | import org.junit.Test;
6 | import org.jvnet.hudson.test.recipes.LocalData;
7 |
8 | import java.io.IOException;
9 | import java.util.UUID;
10 | import java.util.concurrent.ExecutionException;
11 |
12 | import static com.splunk.splunkjenkins.SplunkConfigUtil.verifySplunkSearchResult;
13 |
14 | public class TestResultAdapterTest extends BaseTest {
15 | @LocalData
16 | @Test
17 | public void verifyTestNG() throws ExecutionException, InterruptedException, IOException {
18 | String query = "ExampleIntegrationTest| spath | search \"testsuite.testcase{}.classname\"=ExampleIntegrationTest";
19 | FreeStyleProject project = (FreeStyleProject) j.getInstance().getItem("testng_job1");
20 | String jobName = UUID.randomUUID().toString();
21 | project.renameTo(jobName);
22 | long startTime = System.currentTimeMillis();
23 | Run run = project.scheduleBuild2(0).get();
24 | verifySplunkSearchResult(query, startTime, 1);
25 | //verify test_summary.total number
26 | String buildUrl = run.getUrl();
27 | query = "type=completed test_summary build_url=\"" + buildUrl + "\"| spath | search test_summary.total=2";
28 | verifySplunkSearchResult(query, startTime, 1);
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/Jenkinsfile.mvn:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env groovy
2 | // Parameters to run test
3 | properties([
4 | parameters([string(defaultValue: '', description: 'Splunk host', name: 'host')
5 | , string(defaultValue: '8089', description: 'Splunk management port', name: 'port')
6 | , string(defaultValue: '', description: 'Username', name: 'username')
7 | , string(defaultValue: '', description: 'Password', name: 'password')
8 | , string(defaultValue: '', description: 'Local Repo Url', name: 'repoUrl')
9 | ]),
10 | [$class: 'jenkins.model.BuildDiscarderProperty', strategy: [$class : 'LogRotator',
11 | numToKeepStr : '50',
12 | artifactNumToKeepStr: '20']]
13 | ])
14 |
15 | node {
16 | checkout scm;
17 | def mvnHome = tool name: 'default', type: 'hudson.tasks.Maven$MavenInstallation'
18 | def mvnCmd = "${mvnHome}/bin/mvn";
19 | if (params.password) {
20 | mvnCmd += " -Dhost=${params.host} -Dport=${params.port} -Dpassword=${params.password} -Dusername=${params.username}"
21 | }
22 | mvnCmd += " -Djava.net.preferIPv4Stack=true clean verify"
23 |
24 | if (params.repoUrl) {
25 | mvnCmd += " -Plocal -Drepos.url=${params.repoUrl} deploy cobertura:cobertura"
26 | }
27 | sh mvnCmd
28 | }
29 |
--------------------------------------------------------------------------------
/splunk-devops-extend/src/test/java/com/splunk/splunkjenkins/console/LabelMarkupTextTest.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.console;
2 |
3 | import org.junit.Test;
4 |
5 | import java.io.ByteArrayOutputStream;
6 | import java.io.IOException;
7 | import java.io.OutputStream;
8 |
9 | import static junit.framework.TestCase.assertEquals;
10 |
11 | public class LabelMarkupTextTest{
12 | @Test
13 | public void testMarkup() throws IOException {
14 | LabelMarkupText labelMarkupText=new LabelMarkupText();
15 | labelMarkupText.addMarkup(0,0,
16 | "","test ");
17 | OutputStream outputStream=new ByteArrayOutputStream();
18 | labelMarkupText.write(outputStream);
19 | String outputs=outputStream.toString();
20 | assertEquals("href=url ", outputs);
21 | // test enclosing labels
22 | labelMarkupText.addMarkup(0,0,""," ");
23 | labelMarkupText.addMarkup(0,0,""," ");
24 | outputStream=new ByteArrayOutputStream();
25 | labelMarkupText.write(outputStream);
26 | labelMarkupText.writePreviousLabel(outputStream);
27 | outputs=outputStream.toString();
28 | assertEquals("parallel_label=\"a\" ", outputs);
29 | }
30 | }
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/model/MetaDataConfigItem/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
4 |
35 |
--------------------------------------------------------------------------------
/splunk-devops/src/test/resources/com/splunk/splunkjenkins/TestResultAdapterTest/jobs/testng_job1/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | false
6 |
7 |
8 | true
9 | false
10 | false
11 | false
12 |
13 | false
14 |
15 |
16 | touch testng-reporter-log-result.xml
17 |
18 |
19 |
20 |
21 | testng-reporter-log-result.xml
22 | true
23 | true
24 | false
25 | false
26 | 100
27 | 0
28 | 100
29 | 100
30 | 2
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/splunk-devops/src/test/java/com/splunk/splunkjenkins/HealthMonitorTest.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins;
2 |
3 | import hudson.model.FreeStyleProject;
4 | import hudson.model.Label;
5 | import hudson.model.PeriodicWork;
6 | import hudson.model.TaskListener;
7 | import org.junit.Test;
8 | import org.jvnet.hudson.test.TouchBuilder;
9 |
10 | import java.util.UUID;
11 |
12 | import static com.splunk.splunkjenkins.SplunkConfigUtil.verifySplunkSearchResult;
13 | import static org.junit.Assert.*;
14 |
15 | public class HealthMonitorTest extends BaseTest {
16 |
17 |
18 | @Test
19 | public void execute() throws Exception {
20 | HealthMonitor monitor = PeriodicWork.all().get(HealthMonitor.class);
21 | FreeStyleProject p = j.createFreeStyleProject("test-sleep" + UUID.randomUUID());
22 | p.setAssignedLabel(Label.get("no-such-node"));
23 | p.getBuildersList().add(new TouchBuilder());
24 | long startTime = System.currentTimeMillis();
25 | p.scheduleBuild2(0);
26 | monitor.lastAccessTime=0;
27 | monitor.execute(TaskListener.NULL);
28 | verifySplunkSearchResult("event_tag=queue", startTime, 1);
29 | verifySplunkSearchResult("event_tag=slave", startTime, 1);
30 | }
31 |
32 | @Test
33 | public void getRecurrencePeriod() throws Exception {
34 | long userDefault = 45000;
35 | long period = PeriodicWork.all().get(HealthMonitor.class).getRecurrencePeriod();
36 | assertEquals(userDefault, period);
37 | }
38 |
39 | }
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/utils/CustomSSLConnectionSocketFactory.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.utils;
2 |
3 | import shaded.splk.org.apache.http.HttpHost;
4 | import shaded.splk.org.apache.http.conn.ssl.SSLConnectionSocketFactory;
5 | import shaded.splk.org.apache.http.protocol.HttpContext;
6 |
7 | import javax.net.ssl.HostnameVerifier;
8 | import javax.net.ssl.SSLContext;
9 | import java.io.IOException;
10 | import java.net.InetSocketAddress;
11 | import java.net.Socket;
12 |
13 | import static com.splunk.splunkjenkins.utils.MultipleHostResolver.NAME_DELIMITER;
14 |
15 | public class CustomSSLConnectionSocketFactory extends SSLConnectionSocketFactory {
16 |
17 | public CustomSSLConnectionSocketFactory(SSLContext sslContext, HostnameVerifier hostnameVerifier) {
18 | super(sslContext, hostnameVerifier);
19 | }
20 |
21 | @Override
22 | public Socket connectSocket(int connectTimeout, Socket socket, HttpHost host, InetSocketAddress remoteAddress, InetSocketAddress localAddress, HttpContext context) throws IOException {
23 | if (host.getHostName().contains(NAME_DELIMITER)) {
24 | HttpHost resolvedHost = new HttpHost(remoteAddress.getHostName(), host.getPort(), host.getSchemeName());
25 | return super.connectSocket(connectTimeout, socket, resolvedHost, remoteAddress, localAddress, context);
26 | } else{
27 | return super.connectSocket(connectTimeout, socket, host, remoteAddress, localAddress, context);
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/splunk-devops-extend/src/main/java/com/splunk/splunkjenkins/SplunkPipelineJobProperty.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins;
2 |
3 | import hudson.Extension;
4 | import jenkins.model.OptionalJobProperty;
5 | import org.jenkinsci.Symbol;
6 | import org.jenkinsci.plugins.workflow.job.WorkflowJob;
7 | import org.kohsuke.stapler.DataBoundConstructor;
8 | import org.kohsuke.stapler.DataBoundSetter;
9 |
10 | import edu.umd.cs.findbugs.annotations.CheckForNull;
11 |
12 | @SuppressWarnings("rawtypes")
13 | public class SplunkPipelineJobProperty extends OptionalJobProperty {
14 | Boolean enableDiagram;
15 |
16 | @DataBoundConstructor
17 | public SplunkPipelineJobProperty() {
18 | }
19 |
20 | @CheckForNull
21 | public Boolean getEnableDiagram() {
22 | return enableDiagram;
23 | }
24 |
25 | @DataBoundSetter
26 | public void setEnableDiagram(Boolean enableDiagram) {
27 | this.enableDiagram = enableDiagram;
28 | }
29 |
30 | public boolean isDiagramEnabled() {
31 | return Boolean.TRUE.equals(enableDiagram);
32 | }
33 |
34 | @Override
35 | public String toString() {
36 | return String.format("SplunkPipelineJobProperty{enableDiagram=%s}", enableDiagram);
37 | }
38 |
39 | @Extension
40 | @Symbol("splunkinsJobOption")
41 | public static class DescriptorImpl extends OptionalJobPropertyDescriptor {
42 | @Override
43 | public String getDisplayName() {
44 | return "Opt in data sent to Splunk";
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/splunk-devops/src/test/java/com/splunk/splunkjenkins/utils/PlainTextConsoleUtilsTest.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.utils;
2 |
3 | import hudson.console.ConsoleNote;
4 | import org.junit.Assert;
5 | import org.junit.Test;
6 |
7 | import java.io.ByteArrayOutputStream;
8 |
9 | public class PlainTextConsoleUtilsTest {
10 | private byte[] logText = ("\u001B[8mha:////4KIKPqK5tXCDtTm83KR8dOlkGTotzP4liGbxukwLqvjJAA\u001B[0m[Pipeline] }\n" +
11 | "\u001B[8mha:////4KY/nBiyccGoc9OKNQirqOjwEcX/CTScoTrGPCj/nnzYAAAApB+LCAAAAAAAA\u001B[0m[Pipeline] echo\n" +
12 | "hello branch-2").getBytes();
13 | private String expected = "[Pipeline] }\n[Pipeline] echo\nhello branch-2";
14 |
15 | @Test
16 | public void arrayIndexOf() {
17 | int MAX_FINDS = 2;
18 | int idx = 0;
19 | for (int i = 1; i <= MAX_FINDS + 1; i++) {
20 | idx = PlainTextConsoleUtils.arrayIndexOf(logText, idx + 1, logText.length, ConsoleNote.POSTAMBLE);
21 | if (i > MAX_FINDS) {
22 | Assert.assertTrue("not found in step" + i, idx == -1);
23 | } else {
24 | Assert.assertTrue("find the index in step" + i, idx > 0);
25 | }
26 | }
27 | }
28 |
29 | @Test
30 | public void decodeConsole() {
31 | ByteArrayOutputStream bout = new ByteArrayOutputStream();
32 | PlainTextConsoleUtils.decodeConsole(logText, logText.length, bout);
33 | String console = bout.toString();
34 | Assert.assertEquals(expected, console);
35 | }
36 | }
--------------------------------------------------------------------------------
/splunk-devops/src/test/resources/com/splunk/splunkjenkins/CoverageMetricTest/jobs/Cobertura/workspace/coverage.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | --source
7 | src/main/java
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/links/LinkSplunkAction.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.links;
2 |
3 |
4 | import com.splunk.splunkjenkins.SplunkJenkinsInstallation;
5 | import hudson.model.Action;
6 |
7 | import com.splunk.splunkjenkins.Messages;
8 | import jenkins.model.Jenkins;
9 | import org.apache.commons.lang3.StringUtils;
10 |
11 | public class LinkSplunkAction implements Action {
12 | String query;
13 | String page;
14 | String displayName;
15 |
16 | public LinkSplunkAction(String tab, String query, String displayName) {
17 | this.query = query;
18 | this.page = tab;
19 | this.displayName = displayName;
20 | }
21 |
22 | @Override
23 | public String getIconFileName() {
24 | if (Jenkins.get().hasPermission(ReportAction.SPLUNK_LINK)) {
25 | return Messages.SplunkIconName();
26 | } else {
27 | return null;
28 | }
29 | }
30 |
31 | @Override
32 | public String getDisplayName() {
33 | if (Jenkins.get().hasPermission(ReportAction.SPLUNK_LINK)) {
34 | return displayName;
35 | } else {
36 | return null;
37 | }
38 | }
39 |
40 | @Override
41 | public String getUrlName() {
42 | SplunkJenkinsInstallation instance = SplunkJenkinsInstallation.get();
43 | if (StringUtils.isNotEmpty(query)) {
44 | return instance.getAppUrlOrHelp() + page + "?" + query;
45 | } else {
46 | return instance.getAppUrlOrHelp() + page;
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/model/LoggingJobExtractor.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.model;
2 |
3 | import hudson.ExtensionList;
4 | import hudson.ExtensionPoint;
5 | import hudson.model.Run;
6 | import org.jvnet.tiger_types.Types;
7 |
8 | import java.lang.reflect.ParameterizedType;
9 | import java.lang.reflect.Type;
10 | import java.util.ArrayList;
11 | import java.util.List;
12 | import java.util.Map;
13 |
14 | public abstract class LoggingJobExtractor implements ExtensionPoint {
15 | public final Class targetType;
16 |
17 | public LoggingJobExtractor() {
18 | Type type = Types.getBaseClass(getClass(), LoggingJobExtractor.class);
19 | if (type instanceof ParameterizedType)
20 | targetType = Types.erasure(Types.getTypeArgument(type, 0));
21 | else
22 | throw new IllegalStateException(getClass() + " uses the raw type for extending LoggingJobExtractor");
23 | }
24 |
25 | public abstract Map extract(R r, boolean completed);
26 |
27 | /**
28 | * @return Returns all the registered {@link LoggingJobExtractor}s
29 | */
30 | public static ExtensionList all() {
31 | return ExtensionList.lookup(LoggingJobExtractor.class);
32 | }
33 |
34 | public static List canApply(Run run) {
35 | List extensions = new ArrayList<>();
36 | for (LoggingJobExtractor extendListener : LoggingJobExtractor.all()) {
37 | if (extendListener.targetType.isInstance(run)) {
38 | extensions.add(extendListener);
39 | }
40 | }
41 | return extensions;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/listeners/UserSecurityListener.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.listeners;
2 |
3 | import hudson.Extension;
4 | import hudson.model.User;
5 | import jenkins.security.SecurityListener;
6 | import org.acegisecurity.userdetails.UserDetails;
7 |
8 | import edu.umd.cs.findbugs.annotations.NonNull;
9 |
10 | import static com.splunk.splunkjenkins.utils.LogEventHelper.logUserAction;
11 |
12 | /**
13 | * Note: all the username are user id, not user full name
14 | */
15 | @Extension
16 | public class UserSecurityListener extends SecurityListener {
17 | @Override
18 | protected void authenticated(@NonNull UserDetails details) {
19 | logUserAction(details.getUsername(), "authenticated");
20 | }
21 |
22 | @Override
23 | protected void failedToAuthenticate(@NonNull String username) {
24 | logUserAction(getFullName(username), Messages.audit_user_fail_auth());
25 | }
26 |
27 | @Override
28 | protected void loggedIn(@NonNull String username) {
29 | logUserAction(getFullName(username), Messages.audit_user_login());
30 | }
31 |
32 | @Override
33 | protected void failedToLogIn(@NonNull String username) {
34 | //covered by failedToAuthenticate
35 | }
36 |
37 | @Override
38 | protected void loggedOut(@NonNull String username) {
39 | logUserAction(getFullName(username), Messages.audit_user_logout());
40 | }
41 |
42 | /**
43 | * @param username
44 | * @return full name
45 | */
46 | private String getFullName(String username) {
47 | //user may not exists when user failed to login
48 | User user = User.get(username);
49 | return user.getFullName();
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/splunk-devops-extend/src/test/java/com/splunk/splunkjenkins/SplunkinsDslVariableTest.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins;
2 |
3 | import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
4 | import org.jenkinsci.plugins.workflow.job.WorkflowJob;
5 | import org.jenkinsci.plugins.workflow.job.WorkflowRun;
6 | import org.junit.Before;
7 | import org.junit.ClassRule;
8 | import org.junit.Rule;
9 | import org.junit.Test;
10 | import org.jvnet.hudson.test.BuildWatcher;
11 | import org.jvnet.hudson.test.JenkinsRule;
12 |
13 | import java.util.UUID;
14 |
15 | import static com.splunk.splunkjenkins.SplunkConfigUtil.checkTokenAvailable;
16 |
17 | import static com.splunk.splunkjenkins.SplunkConfigUtil.verifySplunkSearchResult;
18 | import static org.junit.Assert.*;
19 |
20 | public class SplunkinsDslVariableTest {
21 | @ClassRule
22 | public static BuildWatcher buildWatcher = new BuildWatcher();
23 | @Rule
24 | public JenkinsRule r = new JenkinsRule();
25 |
26 | @Before
27 | public void setUp() throws Exception {
28 | org.junit.Assume.assumeTrue(checkTokenAvailable());
29 | }
30 |
31 | @Test
32 | public void testDslScript() throws Exception {
33 | WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "p");
34 | String eventSource = "test_dsl_var_" + UUID.randomUUID().toString();
35 | p.setDefinition(new CpsFlowDefinition("node { splunkins.send('hello', '" + eventSource + "') }"));
36 | WorkflowRun b1 = r.assertBuildStatusSuccess(p.scheduleBuild2(0));
37 | assertFalse(b1.isBuilding());
38 | assertTrue(b1.getDuration() > 0);
39 | String query = "index=" + SplunkConfigUtil.INDEX_NAME + " source=" + eventSource;
40 | int expected = 1;
41 | verifySplunkSearchResult(query, b1.getTimeInMillis(), expected);
42 | }
43 | }
--------------------------------------------------------------------------------
/splunk-devops-extend/src/main/java/com/splunk/splunkjenkins/SplunkinsDslVariable.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins;
2 |
3 |
4 | import hudson.Extension;
5 | import hudson.model.Run;
6 | import hudson.model.TaskListener;
7 | import hudson.util.LogTaskListener;
8 | import org.jenkinsci.plugins.workflow.cps.CpsScript;
9 | import org.jenkinsci.plugins.workflow.cps.GlobalVariable;
10 | import org.jenkinsci.plugins.workflow.job.WorkflowRun;
11 |
12 | import edu.umd.cs.findbugs.annotations.NonNull;
13 | import java.lang.reflect.Field;
14 | import java.util.Map;
15 | import java.util.logging.Level;
16 | import java.util.logging.Logger;
17 |
18 | import static com.splunk.splunkjenkins.utils.LogEventHelper.getBuildVariables;
19 |
20 | @Extension(optional = true)
21 | public class SplunkinsDslVariable extends GlobalVariable {
22 | @NonNull
23 | @Override
24 | public String getName() {
25 | return "splunkins";
26 | }
27 |
28 | @NonNull
29 | @Override
30 | public Object getValue(@NonNull CpsScript script) throws Exception {
31 | Run, ?> build = script.$build();
32 | if (build == null) {
33 | throw new IllegalStateException("cannot find associated build");
34 | }
35 | // try to access WorkflowRun.listener
36 | TaskListener listener = TaskListener.NULL;
37 | try {
38 | Field field = WorkflowRun.class.getDeclaredField("listener");
39 | field.setAccessible(true);
40 | listener = (TaskListener) field.get(build);
41 | } catch (Exception e) {
42 | listener = new LogTaskListener(Logger.getLogger(SplunkinsDslVariable.class.getName()), Level.INFO);
43 | }
44 | Map buildParameters = getBuildVariables(build);
45 | RunDelegate delegate = new RunDelegate(build, buildParameters, listener);
46 | return delegate;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/links/RunActionFactory.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.links;
2 |
3 | import com.splunk.splunkjenkins.SplunkJenkinsInstallation;
4 | import com.splunk.splunkjenkins.utils.LogEventHelper;
5 | import hudson.Extension;
6 | import hudson.model.Action;
7 | import hudson.model.Job;
8 | import hudson.model.Run;
9 | import jenkins.model.TransientActionFactory;
10 |
11 | import edu.umd.cs.findbugs.annotations.NonNull;
12 | import java.io.File;
13 | import java.util.Collection;
14 | import java.util.Collections;
15 |
16 | @SuppressWarnings("unused")
17 | @Extension
18 | public class RunActionFactory extends TransientActionFactory {
19 | @Override
20 | public Class type() {
21 | return Run.class;
22 | }
23 |
24 | @NonNull
25 | @Override
26 | public Collection extends Action> createFor(@NonNull Run target) {
27 | Job job = target.getParent();
28 | LogEventHelper.UrlQueryBuilder builder = new LogEventHelper.UrlQueryBuilder()
29 | .putIfAbsent("job", job.getFullName())
30 | .putIfAbsent("build", target.getNumber() + "");
31 | File junitFile = new File(target.getRootDir(), "junitResult.xml");
32 | if (junitFile.exists() || job.getClass().getName().startsWith("hudson.maven.")) {
33 | // test page is using master query param instead of host
34 | builder.putIfAbsent("master", SplunkJenkinsInstallation.get().getMetadataHost());
35 | String query = builder.build();
36 | return Collections.singleton(new LinkSplunkAction("testAnalysis", query, "Splunk"));
37 | }
38 | String query = builder.putIfAbsent("type", "build")
39 | .putIfAbsent("host", SplunkJenkinsInstallation.get().getMetadataHost()).build();
40 | return Collections.singleton(new LinkSplunkAction("build", query, "Splunk"));
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/SplunkJenkinsInstallation/help-groovyBinding.html:
--------------------------------------------------------------------------------
1 |
2 |
Provide a groovy script to customize event process,
3 | will be executed when job is completed
4 |
5 |
6 |
7 | You can customize the events sent to splunk by call:
8 |
9 | //send job metadata and junit reports with page size set to 50 (each event contains max 50 test cases)
10 | splunkins.sendTestReport(50)
11 | //send coverage, each event contains max 50 class metrics
12 | splunkins.sendCoverageReport(50)
13 | //send all logs from workspace to splunk, with each file size limits to 10MB
14 | splunkins.archive("**/*.log", null, false, "10MB")
15 |
16 |
17 | The groovy script can use the variable
splunkins , which provides access to the following objects and methods:
18 |
19 |
20 | Action getAction(Class type);
21 | Action getActionByClassName(String className);
22 | //send message to splunk
23 | boolean send(Object message);
24 | //Archive all configured artifacts from slave, with each file size limit to 10MB, using ant patterns defined in http://ant.apache.org/manual/Types/fileset.html
25 | archive(String includes, String excludes = null, boolean uploadFromSlave = false, String fileSizeLimit = "")
26 | //will send build parameters as metadata and with the object returned from closure to splunk
27 | sendReport(Closure closure)
28 | //a junit report with summary of passes,failures,skips and details of testcase
29 | getJunitReport()
30 | //a a list of junit report each with summary of passes,failures,skips and details of testcase
31 | //each report contains max pageSize testcases
32 | getJunitReport(int pageSize)
33 | sendTestReport(pageSize) //send Test report, with pagination support
34 | sendCoverageReport(pageSize) //send coverage report, with pagination support
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/splunk-devops/src/test/java/com/splunk/splunkjenkins/SplunkSendJsonFileTest.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins;
2 |
3 | import hudson.model.FreeStyleProject;
4 | import hudson.model.Run;
5 | import hudson.tasks.Shell;
6 | import org.junit.Test;
7 |
8 | import java.util.UUID;
9 |
10 | import static com.splunk.splunkjenkins.SplunkConfigUtil.verifySplunkSearchResult;
11 |
12 | public class SplunkSendJsonFileTest extends BaseTest {
13 |
14 | private Run createBuild(String textContent, String fileName) throws Exception {
15 | FreeStyleProject project = j.createFreeStyleProject("publisher_test" + UUID.randomUUID());
16 | project.getBuildersList().add(new Shell("cat > " + fileName + " <<'EOF'\n" + textContent + "\nEOF"));
17 | SplunkArtifactNotifier splunkArtifactNotifier = new SplunkArtifactNotifier("*", null, false, false, "10MB");
18 | project.getPublishersList().add(splunkArtifactNotifier);
19 | Run build = project.scheduleBuild2(0).get();
20 | j.assertBuildStatusSuccess(build);
21 | return build;
22 | }
23 |
24 |
25 | @Test
26 | public void testSendJsonFile() throws Exception {
27 | String name = "client_" + UUID.randomUUID();
28 | String jsonText = "{\n\"name\":\n\"" + name + "\"\n}";
29 | String query = "index=" + SplunkConfigUtil.INDEX_NAME + " name=" + name;
30 | Run build = createBuild(jsonText, "user.json");
31 | verifySplunkSearchResult(query, build.getTimeInMillis(), 1);
32 | }
33 |
34 | @Test
35 | public void testSendTextFile() throws Exception {
36 | String name = "client_" + UUID.randomUUID();
37 | String jsonText = "line1: " + name + "\nline2: " + name;
38 | String query = "index=" + SplunkConfigUtil.INDEX_NAME + " " + name;
39 | Run build = createBuild(jsonText, "user.txt");
40 | verifySplunkSearchResult(query, build.getTimeInMillis(), 2);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/splunk-devops-extend/src/main/java/com/splunk/splunkjenkins/console/ConsoleRecordCacheUtils.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.console;
2 |
3 | import com.splunk.splunkjenkins.model.EventRecord;
4 | import com.splunk.splunkjenkins.model.EventType;
5 | import com.splunk.splunkjenkins.utils.SplunkLogService;
6 | import jenkins.util.JenkinsJVM;
7 |
8 | import java.util.ArrayList;
9 | import java.util.List;
10 | import java.util.concurrent.ConcurrentLinkedQueue;
11 | import java.util.logging.Level;
12 | import java.util.logging.Logger;
13 |
14 | public class ConsoleRecordCacheUtils {
15 | private static final int CACHED_LINES_LIMIT;
16 | private transient static final Logger LOGGER = Logger.getLogger(SplunkConsoleTaskListenerDecorator.class.getName());
17 | private transient static final ConcurrentLinkedQueue consoleQueue = new ConcurrentLinkedQueue<>();
18 |
19 | static {
20 | if (JenkinsJVM.isJenkinsJVM()) {
21 | CACHED_LINES_LIMIT = 200;
22 | } else {
23 | CACHED_LINES_LIMIT = 10;
24 | }
25 | }
26 |
27 | public static void enqueue(EventRecord record) {
28 | boolean added = consoleQueue.add(record);
29 | if (!added) {
30 | LOGGER.warning("failed to add log " + record.getMessageString());
31 | } else if (consoleQueue.size() > CACHED_LINES_LIMIT) {
32 | flushLog();
33 | }
34 | }
35 |
36 | public static void flushLog() {
37 | EventRecord record;
38 | List pendingRecords = new ArrayList<>();
39 | try {
40 | while ((record = consoleQueue.poll()) != null) {
41 | pendingRecords.add(record);
42 | }
43 | SplunkLogService.getInstance().sendBatch(pendingRecords, EventType.CONSOLE_LOG);
44 | } catch (Throwable ex) {
45 | LOGGER.log(Level.SEVERE, "flush log error", ex);
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/splunk-devops/src/test/resources/com/splunk/splunkjenkins/CoverageMetricTest/jobs/JaCoCo/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | false
6 |
7 |
8 | true
9 | false
10 | false
11 | false
12 |
13 | false
14 |
15 |
16 | **/**.exec
17 | **/classes
18 | **/java
19 |
20 |
21 | 0
22 | 0
23 | 0
24 | 0
25 | 0
26 | 0
27 | 0
28 | 0
29 | 0
30 | 0
31 | 0
32 | 0
33 | false
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/links/ReportAction.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.links;
2 |
3 | import com.splunk.splunkjenkins.Messages;
4 | import com.splunk.splunkjenkins.SplunkJenkinsInstallation;
5 | import hudson.Extension;
6 | import hudson.model.RootAction;
7 | import hudson.security.Permission;
8 | import hudson.security.PermissionGroup;
9 | import hudson.security.PermissionScope;
10 | import jenkins.model.Jenkins;
11 |
12 | @SuppressWarnings("unused")
13 | @Extension
14 | public class ReportAction implements RootAction {
15 |
16 | /**
17 | * Permission group for Splunk Link related permissions.
18 | */
19 | public static final PermissionGroup PERMISSIONS =
20 | new PermissionGroup(ReportAction.class, Messages._PermissionGroup());
21 | /**
22 | * Permission to get the Splunk link displayed.
23 | */
24 | public static final Permission SPLUNK_LINK = new Permission(PERMISSIONS,
25 | "SplunkLink", Messages._PluginViewPermission_Description(), Jenkins.ADMINISTER, PermissionScope.JENKINS);
26 |
27 |
28 | @Override
29 | public String getIconFileName() {
30 | if (Jenkins.getInstance().hasPermission(ReportAction.SPLUNK_LINK)){
31 | return Messages.SplunkIconName();
32 | }
33 | return null;
34 | }
35 |
36 | @Override
37 | public String getDisplayName() {
38 | if (Jenkins.getInstance().hasPermission(ReportAction.SPLUNK_LINK)){
39 | return "Splunk";
40 | }
41 | return null;
42 | }
43 |
44 | @Override
45 | public String getUrlName() {
46 | if (Jenkins.getInstance().hasPermission(ReportAction.SPLUNK_LINK)){
47 | SplunkJenkinsInstallation instance = SplunkJenkinsInstallation.get();
48 | return instance.getAppUrlOrHelp() + "overview?overview_jenkinsmaster=" + instance.getMetadataHost();
49 | }
50 | return null;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/splunk-devops-extend/src/test/java/com/splunk/splunkjenkins/SplunkLogFileStepTest.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins;
2 |
3 | import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
4 | import org.jenkinsci.plugins.workflow.job.WorkflowJob;
5 | import org.jenkinsci.plugins.workflow.job.WorkflowRun;
6 | import org.junit.Before;
7 | import org.junit.ClassRule;
8 | import org.junit.Rule;
9 | import org.junit.Test;
10 | import org.jvnet.hudson.test.BuildWatcher;
11 | import org.jvnet.hudson.test.JenkinsRule;
12 |
13 | import java.util.UUID;
14 |
15 | import static com.splunk.splunkjenkins.SplunkConfigUtil.checkTokenAvailable;
16 | import static com.splunk.splunkjenkins.SplunkConfigUtil.verifySplunkSearchResult;
17 | import static org.junit.Assert.*;
18 |
19 | public class SplunkLogFileStepTest {
20 | @ClassRule
21 | public static BuildWatcher buildWatcher = new BuildWatcher();
22 | @Rule
23 | public JenkinsRule r = new JenkinsRule();
24 | String fileName = UUID.randomUUID().toString() + ".log";
25 |
26 | private String jobScript = "node{\n" +
27 | "sh \"echo testjob\";\n" +
28 | "sh \"echo 'hello world' > " + fileName + "\";\n" +
29 | "sendSplunkFile includes: \"*.log\";\n" +
30 | "}";
31 |
32 | @Before
33 | public void setUp() throws Exception {
34 | org.junit.Assume.assumeTrue(checkTokenAvailable());
35 | }
36 |
37 | @Test
38 | public void testSendFile() throws Exception {
39 | long startTime = System.currentTimeMillis();
40 | WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "p");
41 | p.setDefinition(new CpsFlowDefinition(jobScript));
42 | WorkflowRun b1 = r.assertBuildStatusSuccess(p.scheduleBuild2(0));
43 | assertFalse(b1.isBuilding());
44 | r.assertLogContains("testjob", b1);
45 | assertTrue(b1.getDuration() > 0);
46 | //check log
47 | verifySplunkSearchResult("source=" + b1.getUrl() + fileName, startTime, 1);
48 | }
49 | }
--------------------------------------------------------------------------------
/splunk-devops-extend/src/main/java/com/splunk/splunkjenkins/console/SplunkConsoleTaskListenerDecorator.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.console;
2 |
3 | import com.splunk.splunkjenkins.utils.RemoteUtils;
4 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
5 | import jenkins.util.JenkinsJVM;
6 | import org.jenkinsci.plugins.workflow.job.WorkflowRun;
7 | import org.jenkinsci.plugins.workflow.log.TaskListenerDecorator;
8 |
9 | import edu.umd.cs.findbugs.annotations.NonNull;
10 | import java.io.IOException;
11 | import java.io.OutputStream;
12 | import java.util.Map;
13 |
14 | public class SplunkConsoleTaskListenerDecorator extends TaskListenerDecorator {
15 | private static final long serialVersionUID = 1L;
16 | @SuppressFBWarnings("SE_TRANSIENT_FIELD_NOT_RESTORED")
17 | transient PipelineConsoleDecoder decoder;
18 | // it is optional but not use Optional since Optional is not serializable
19 | Map remoteSplunkinsConfig = null;
20 | String source;
21 |
22 | public SplunkConsoleTaskListenerDecorator(WorkflowRun run) {
23 | this.decoder = new PipelineConsoleDecoder(run);
24 | this.source = run.getUrl() + "console";
25 | }
26 |
27 | @NonNull
28 | @Override
29 | public OutputStream decorate(@NonNull OutputStream outputStream) throws IOException {
30 | if (!JenkinsJVM.isJenkinsJVM()) {
31 | if (remoteSplunkinsConfig != null) {
32 | RemoteUtils.initSplunkConfigOnAgent(remoteSplunkinsConfig);
33 | } else {
34 | // no-op
35 | return outputStream;
36 | }
37 | }
38 | if (decoder == null) {
39 | // resume from restart
40 | decoder = new PipelineConsoleDecoder(null);
41 | }
42 | //called for every step
43 | return new LabelConsoleLineStream(outputStream, source, decoder);
44 | }
45 |
46 | protected void setRemoteSplunkinsConfig(Map remoteSplunkinsConfig) {
47 | this.remoteSplunkinsConfig = remoteSplunkinsConfig;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/splunk-devops/src/test/resources/com/splunk/splunkjenkins/CoverageMetricTest/jobs/Clover/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | false
6 |
7 |
8 | false
9 |
10 |
11 | false
12 | false
13 |
14 |
15 | 0
16 | 0
17 |
18 | false
19 | project
20 |
21 |
22 |
23 | true
24 | false
25 | false
26 | false
27 |
28 | false
29 |
30 |
31 |
32 |
33 | clover.xml
34 |
35 | 70
36 | 80
37 | 80
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/splunk-devops-extend/src/test/java/com/splunk/splunkjenkins/PipelineExecuteDiagramTest.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins;
2 |
3 | import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
4 | import org.jenkinsci.plugins.workflow.job.WorkflowJob;
5 | import org.jenkinsci.plugins.workflow.job.WorkflowRun;
6 | import org.junit.Before;
7 | import org.junit.ClassRule;
8 | import org.junit.Rule;
9 | import org.junit.Test;
10 | import org.jvnet.hudson.test.BuildWatcher;
11 | import org.jvnet.hudson.test.JenkinsRule;
12 |
13 | import static com.splunk.splunkjenkins.SplunkConfigUtil.checkTokenAvailable;
14 | import static com.splunk.splunkjenkins.SplunkConfigUtil.verifySplunkSearchResult;
15 | import static org.junit.Assert.assertFalse;
16 | import static org.junit.Assert.assertTrue;
17 |
18 | public class PipelineExecuteDiagramTest {
19 | @ClassRule
20 | public static BuildWatcher buildWatcher = new BuildWatcher();
21 | @Rule
22 | public JenkinsRule r = new JenkinsRule();
23 | private String jobScript = "properties([splunkinsJobOption(enableDiagram: true)])\n" +
24 | "stage(\"unit-test\"){\n" +
25 | " node{\n" +
26 | " echo \"hello\"\n" +
27 | " echo \"hello world2\"\n" +
28 | " }\n" +
29 | "}";
30 |
31 | @Before
32 | public void setUp() throws Exception {
33 | org.junit.Assume.assumeTrue(checkTokenAvailable());
34 | }
35 |
36 | @Test
37 | public void testExecuteDiagram() throws Exception {
38 | long startTime = System.currentTimeMillis();
39 | WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "testExecuteDiagram");
40 | p.setDefinition(new CpsFlowDefinition(jobScript));
41 | WorkflowRun b1 = r.assertBuildStatusSuccess(p.scheduleBuild2(0));
42 | assertFalse(b1.isBuilding());
43 | r.assertLogContains("hello", b1);
44 | assertTrue(b1.getDuration() > 0);
45 | //check exec_node
46 | verifySplunkSearchResult("source=" + b1.getUrl() + "graphviz", startTime, 1);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/model/JunitTestCaseGroup.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.model;
2 |
3 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
4 | import hudson.Util;
5 | import hudson.tasks.test.TestResult;
6 |
7 | import java.io.Serializable;
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | @SuppressFBWarnings("URF_UNREAD_FIELD")
12 | public class JunitTestCaseGroup implements Serializable{
13 | int failures;
14 | int passes;
15 | int skips;
16 | int total;
17 | float duration;
18 | //alias, fields for json serialization
19 | int tests;
20 | float time;
21 | //backward compatible with junit3 xml which has errors field
22 | int errors = 0;
23 | List testcase = new ArrayList<>();
24 |
25 | public void add(TestResult result) {
26 | this.failures += result.getFailCount();
27 | this.passes += result.getPassCount();
28 | this.skips += result.getSkipCount();
29 | this.total += result.getTotalCount();
30 | this.duration += result.getDuration();
31 | //update alias
32 | this.tests = this.total;
33 | this.time = this.duration;
34 | this.testcase.add(result);
35 | }
36 |
37 | public int getFailures() {
38 | return failures;
39 | }
40 |
41 | public int getPasses() {
42 | return passes;
43 | }
44 |
45 | public int getSkips() {
46 | return skips;
47 | }
48 |
49 | public int getTotal() {
50 | return total;
51 | }
52 |
53 | public float getDuration() {
54 | return duration;
55 | }
56 |
57 | public List getTestcase() {
58 | return testcase;
59 | }
60 |
61 | @Override
62 | public String toString() {
63 | return "failures: " + failures +
64 | ", passes: " + passes +
65 | ", skips: " + skips +
66 | ", errors: " + errors +
67 | ", total: " + total +
68 | ", duration: " + Util.getTimeSpanString(1000L * (long) duration);
69 |
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/listeners/LoggingItemListener.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.listeners;
2 |
3 | import com.splunk.splunkjenkins.utils.SplunkLogService;
4 | import hudson.Extension;
5 | import hudson.model.Item;
6 | import hudson.model.listeners.ItemListener;
7 |
8 | import java.io.File;
9 |
10 | import static com.splunk.splunkjenkins.utils.LogEventHelper.getRelativeJenkinsHomePath;
11 | import static com.splunk.splunkjenkins.utils.LogEventHelper.getUserName;
12 | import static com.splunk.splunkjenkins.utils.LogEventHelper.logUserAction;
13 |
14 | @Extension
15 | public class LoggingItemListener extends ItemListener {
16 | @Override
17 | public void onCreated(Item item) {
18 | logUserAction(getUserName(), Messages.audit_create_item(getConfigPath(item)));
19 | }
20 |
21 | @Override
22 | public void onCopied(Item src, Item item) {
23 | logUserAction(getUserName(), Messages.audit_cloned_item(getConfigPath(item), getConfigPath(src)));
24 | }
25 |
26 | @Override
27 | public void onDeleted(Item item) {
28 | logUserAction(getUserName(), Messages.audit_delete_item(getConfigPath(item)));
29 | }
30 |
31 | @Override
32 | public void onRenamed(Item item, String oldName, String newName) {
33 | //no-op, we use onLocationChanged
34 | }
35 |
36 | @Override
37 | public void onUpdated(Item item) {
38 | //prior to delete, makeDisabled was called and onUpdated is triggered
39 | logUserAction(getUserName(), Messages.audit_update_item(getConfigPath(item)));
40 | }
41 |
42 | @Override
43 | public void onLocationChanged(Item item, String oldFullName, String newFullName) {
44 | logUserAction(getUserName(), Messages.audit_rename_item(oldFullName, newFullName));
45 | }
46 |
47 | private String getConfigPath(Item item) {
48 | if (item == null) {
49 | return "unknown";
50 | }
51 | return getRelativeJenkinsHomePath(item.getRootDir() + File.separator + "config.xml");
52 | }
53 |
54 | @Override
55 | public void onBeforeShutdown() {
56 | SplunkLogService.getInstance().stopWorker();
57 | SplunkLogService.getInstance().releaseConnection();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/listeners/LoggingComputerListener.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.listeners;
2 |
3 | import com.splunk.splunkjenkins.SplunkJenkinsInstallation;
4 | import com.splunk.splunkjenkins.utils.SplunkLogService;
5 | import hudson.Extension;
6 | import hudson.model.Computer;
7 | import hudson.model.TaskListener;
8 | import hudson.slaves.ComputerListener;
9 | import hudson.slaves.OfflineCause;
10 |
11 | import edu.umd.cs.findbugs.annotations.CheckForNull;
12 | import edu.umd.cs.findbugs.annotations.NonNull;
13 | import java.io.IOException;
14 | import java.util.Map;
15 |
16 | import static com.splunk.splunkjenkins.Constants.EVENT_CAUSED_BY;
17 | import static com.splunk.splunkjenkins.model.EventType.SLAVE_INFO;
18 | import static com.splunk.splunkjenkins.utils.LogEventHelper.getComputerStatus;
19 |
20 | @SuppressWarnings("unused")
21 | @Extension
22 | public class LoggingComputerListener extends ComputerListener {
23 | @Override
24 | public void onOnline(Computer c, TaskListener listener) throws IOException, InterruptedException {
25 | updateStatus(c, "Online");
26 | listener.getLogger().flush();
27 | }
28 |
29 | @Override
30 | public void onOffline(@NonNull Computer c, @CheckForNull OfflineCause cause) {
31 | updateStatus(c, "Offline");
32 | }
33 |
34 | @Override
35 | public void onTemporarilyOnline(Computer c) {
36 | updateStatus(c, "Temporarily Online");
37 | }
38 |
39 | @Override
40 | public void onTemporarilyOffline(Computer c, OfflineCause cause) {
41 | updateStatus(c, "Temporarily Offline");
42 | }
43 |
44 | @Override
45 | public void onLaunchFailure(Computer c, TaskListener taskListener) throws IOException, InterruptedException {
46 | updateStatus(c, "Launch Failure");
47 | taskListener.getLogger().flush();
48 | }
49 |
50 | private void updateStatus(Computer c, String eventSource) {
51 | if (SplunkJenkinsInstallation.get().isEventDisabled(SLAVE_INFO)) {
52 | return;
53 | }
54 | Map slaveInfo = getComputerStatus(c);
55 | slaveInfo.put(EVENT_CAUSED_BY, eventSource);
56 | SplunkLogService.getInstance().send(slaveInfo, SLAVE_INFO);
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/splunk-devops/src/test/java/com/splunk/splunkjenkins/utils/LogEventHelperTest.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.utils;
2 |
3 | import com.splunk.splunkjenkins.BaseTest;
4 | import hudson.model.Label;
5 | import hudson.model.Slave;
6 | import org.junit.*;
7 | import org.jvnet.hudson.test.JenkinsRule;
8 |
9 | import java.util.List;
10 | import java.util.Map;
11 | import java.util.concurrent.TimeUnit;
12 | import java.util.logging.Level;
13 |
14 | import static com.splunk.splunkjenkins.SplunkConfigUtil.checkTokenAvailable;
15 | import static org.junit.Assert.*;
16 |
17 | public class LogEventHelperTest extends BaseTest {
18 | private static final java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(LogEventHelper.class.getName());
19 |
20 | @Test
21 | public void parseFileSize() throws Exception {
22 | long oneMB = 1024 * 1024;
23 | long twoKB = 2 * 1024;
24 | assertEquals(oneMB, LogEventHelper.parseFileSize("1MB"));
25 | assertEquals(512 * 1024, LogEventHelper.parseFileSize("0.5MB"));
26 | assertEquals(twoKB, LogEventHelper.parseFileSize("2KB"));
27 | assertEquals(123535, LogEventHelper.parseFileSize("123535"));
28 | assertEquals(0, LogEventHelper.parseFileSize("12s"));
29 | }
30 |
31 | @Test
32 | public void testSlaveStats() throws Exception {
33 | Slave slave = j.createOnlineSlave();
34 | Map> slaveStats = LogEventHelper.getSlaveStats();
35 | assertTrue("should not be empty", !slaveStats.isEmpty());
36 | String slaveName = slave.getNodeName();
37 | String monitorName = "ClockMonitor";
38 | long timeToWait = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(1);
39 | boolean hasMonitorData = false;
40 | while (System.currentTimeMillis() < timeToWait) {
41 | slaveStats = LogEventHelper.getSlaveStats();
42 | LOG.log(Level.FINER, slaveStats.toString());
43 | if (slaveStats.containsKey(slaveName)) {
44 | hasMonitorData = slaveStats.get(slaveName).containsKey(monitorName);
45 |
46 | }
47 | if (hasMonitorData) {
48 | break;
49 | }
50 | }
51 | assertTrue(hasMonitorData);
52 | }
53 | }
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/LoggingInitStep.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins;
2 |
3 | import com.splunk.splunkjenkins.utils.LogEventHelper;
4 | import hudson.init.Initializer;
5 | import hudson.util.PluginServletFilter;
6 | import jenkins.util.Timer;
7 |
8 | import javax.servlet.ServletException;
9 | import java.util.concurrent.TimeUnit;
10 | import java.util.logging.Handler;
11 | import java.util.logging.Level;
12 | import java.util.logging.Logger;
13 |
14 | import static hudson.init.InitMilestone.JOB_LOADED;
15 |
16 | @edu.umd.cs.findbugs.annotations.SuppressFBWarnings("LG_LOST_LOGGER_DUE_TO_WEAK_REFERENCE")
17 | public class LoggingInitStep {
18 | private final static String rootLoggerName = "";
19 |
20 | @Initializer(after = JOB_LOADED)
21 | public static void setupSplunkJenkins() {
22 | Timer.get().schedule(new Runnable() {
23 | @Override
24 | public void run() {
25 | registerHandler();
26 | }
27 | }, 30, TimeUnit.SECONDS);
28 | }
29 |
30 | protected static void registerHandler() {
31 | Handler[] handlers = Logger.getLogger(rootLoggerName).getHandlers();
32 | for (Handler handler : handlers) {
33 | if (handler instanceof JdkSplunkLogHandler) {
34 | // already registered
35 | return;
36 | }
37 | }
38 | //only log warning message for HealthMonitor which runs every 20s
39 | Logger.getLogger(HealthMonitor.class.getName()).setLevel(Level.WARNING);
40 | Logger.getLogger(rootLoggerName).addHandler(JdkSplunkLogHandler.LogHolder.LOG_HANDLER);
41 | //init plugin
42 | SplunkJenkinsInstallation.get().updateCache();
43 | SplunkJenkinsInstallation.markComplete(true);
44 | Logger.getLogger(LoggingInitStep.class.getName()).info("plugin splunk-devops version " + LogEventHelper.getBuildVersion() + " loaded");
45 | // check filter
46 | if (Constants.ENABLE_POST_LOGGER) {
47 | try {
48 | PluginServletFilter.addFilter(new WebPostAccessLogger());
49 | } catch (ServletException e) {
50 | Logger.getLogger(LoggingInitStep.class.getName()).warning("failed to register splunk web access logger");
51 | }
52 | }
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/splunk-devops/src/test/java/com/splunk/splunkjenkins/CoverageMetricTest.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins;
2 |
3 | import hudson.model.AbstractBuild;
4 | import hudson.model.FreeStyleProject;
5 | import org.junit.Test;
6 | import org.jvnet.hudson.test.recipes.LocalData;
7 |
8 | import java.util.UUID;
9 |
10 | import static com.splunk.splunkjenkins.SplunkConfigUtil.verifySplunkSearchResult;
11 |
12 | public class CoverageMetricTest extends BaseTest {
13 | @LocalData
14 | @Test
15 | public void getCoberturaReport() throws Exception {
16 | String jobName = "Cobertura";
17 | getCoverageReport(jobName, 50);
18 | }
19 |
20 | @LocalData
21 | @Test
22 | public void getCloverReport() throws Exception {
23 | String jobName = "Clover";
24 | getCoverageReport(jobName, 50);
25 | }
26 |
27 | @LocalData
28 | @Test
29 | public void getJaCoCoReport() throws Exception {
30 | String jobName = "JaCoCo";
31 | getCoverageReport(jobName, 50);
32 | }
33 |
34 | public void getCoverageReport(String jobName, int methodPercentage) throws Exception {
35 | FreeStyleProject project = (FreeStyleProject) j.getInstance().getItem(jobName);
36 | String newName = UUID.randomUUID().toString();
37 | project.renameTo(newName);
38 | long startTime = System.currentTimeMillis();
39 | AbstractBuild build = project.scheduleBuild2(0).get();
40 | //verify coverage summary
41 | String query = "event_tag=job_event build_url=" + build.getUrl() + " \"coverage.methods\" >= " + methodPercentage;
42 | verifySplunkSearchResult(query, startTime, 1);
43 | //verify details
44 | query = "source=\"unit_test/coverage\" build_url=" + build.getUrl() + "|"
45 | + "rename \"coverage{}.methods_percentage\" as methods |mvexpand methods " +
46 | "|search methods>=" + methodPercentage;
47 | verifySplunkSearchResult(query, startTime, 1);
48 | //check total and covered number
49 | query = "splunk_server=local index=plugin_sandbox build_url=" + build.getUrl() +
50 | " source=\"unit_test/coverage\" \"com.mycompany\"\n" +
51 | "|spath output=coverage_json path=coverage{}|mvexpand coverage_json\n" +
52 | "|spath input=coverage_json|where name=\"com.mycompany\" and methods_total>methods_covered|table methods*";
53 | verifySplunkSearchResult(query, startTime, 1);
54 | }
55 | }
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/model/JunitResultAdapter.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.model;
2 |
3 | import hudson.Extension;
4 | import hudson.tasks.junit.CaseResult;
5 | import hudson.tasks.junit.SuiteResult;
6 | import hudson.tasks.junit.TestResultAction;
7 |
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | @Extension(optional = true)
12 | public class JunitResultAdapter extends AbstractTestResultAdapter {
13 | @Override
14 | public List getTestResult(TestResultAction resultAction) {
15 | List caseResults = new ArrayList<>();
16 | hudson.tasks.junit.TestResult result = resultAction.getResult();
17 | for (SuiteResult suite : result.getSuites()) {
18 | for (CaseResult testCase : suite.getCases()) {
19 | caseResults.add(convert(testCase, suite.getName()));
20 | }
21 | }
22 | return caseResults;
23 | }
24 |
25 | /**
26 | * @param methodResult
27 | * @return unified test case result
28 | */
29 | private TestCaseResult convert(CaseResult methodResult, String suiteName) {
30 | String buildUrl = "";
31 | if (methodResult.getRun() != null) {
32 | buildUrl = methodResult.getRun().getUrl();
33 | }
34 | TestCaseResult testCaseResult = new TestCaseResult();
35 | testCaseResult.setTestName(methodResult.getName());
36 | testCaseResult.setUniqueName(methodResult.getFullName());
37 | testCaseResult.setDuration(methodResult.getDuration());
38 | testCaseResult.setClassName(methodResult.getClassName());
39 | testCaseResult.setErrorDetails(methodResult.getErrorDetails());
40 | testCaseResult.setErrorStackTrace(methodResult.getErrorStackTrace());
41 | testCaseResult.setSkippedMessage(methodResult.getSkippedMessage());
42 | testCaseResult.setFailedSince(methodResult.getFailedSince());
43 | testCaseResult.setStderr(trimToLimit(methodResult.getStderr(), methodResult.getFullName(), buildUrl));
44 | testCaseResult.setStdout(trimToLimit(methodResult.getStdout(), methodResult.getFullName(), buildUrl));
45 | testCaseResult.setGroupName(suiteName);
46 | TestStatus status = TestStatus.SKIPPED;
47 | if (!methodResult.isSkipped()) {
48 | status = methodResult.isPassed() ? TestStatus.PASSED : TestStatus.FAILURE;
49 | }
50 | testCaseResult.setStatus(status);
51 | return testCaseResult;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/splunk-devops-extend/src/main/java/com/splunk/splunkjenkins/console/ConsoleNoteHandler.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.console;
2 |
3 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
4 | import org.jsoup.Jsoup;
5 | import org.jsoup.nodes.Attributes;
6 | import org.jsoup.nodes.Document;
7 | import org.jsoup.nodes.Element;
8 |
9 | public class ConsoleNoteHandler {
10 |
11 | private String href;
12 | private String nodeId;
13 | private String startId;
14 | private String enclosingId;
15 | private String label;
16 |
17 | public String getHref() {
18 | return href;
19 | }
20 |
21 | public String getNodeId() {
22 | return nodeId;
23 | }
24 |
25 | public String getStartId() {
26 | return startId;
27 | }
28 |
29 | public String getEnclosingId() {
30 | return enclosingId;
31 | }
32 |
33 | public String getLabel() {
34 | return label;
35 | }
36 |
37 | /**
38 | * parse first html tag or with nodeId attribute
39 | *
40 | * @param tag
41 | * @see org.jenkinsci.plugins.workflow.job.console.NewNodeConsoleNote
42 | * @see hudson.console.HyperlinkNote
43 | * @see org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApprovalNote
44 | */
45 | @SuppressFBWarnings("SF_SWITCH_NO_DEFAULT")
46 | public void read(String tag) {
47 | Document doc = Jsoup.parse(tag);
48 | Element nodeEle = doc.getElementsByTag("a").first();
49 | if (nodeEle == null) {
50 | nodeEle = doc.getElementsByTag("span").first();
51 | }
52 | if (nodeEle == null) {
53 | return;
54 | }
55 | if (nodeEle.attributesSize() == 0) {
56 | return;
57 | }
58 | Attributes attrs = nodeEle.attributes();
59 | href = getAttribute(attrs, "href");
60 | nodeId = getAttribute(attrs, "nodeid");
61 | startId = getAttribute(attrs, "startId");
62 | enclosingId = getAttribute(attrs, "enclosingId");
63 | label = getAttribute(attrs, "label");
64 | }
65 |
66 |
67 | private String getAttribute(Attributes attrs, String attrKey) {
68 | String key = attrKey.toLowerCase();
69 | // In the HTML syntax, attribute names may be written with any mix of lower- and uppercase letters that, when converted to all-lowercase, matches the attribute's name; attribute names are case-insensitive.
70 | if (attrs.hasKey(key)) {
71 | return attrs.get(key);
72 | } else {
73 | return null;
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/utils/PlainTextConsoleUtils.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.utils;
2 |
3 | import hudson.console.ConsoleNote;
4 |
5 | import java.io.ByteArrayOutputStream;
6 |
7 | public class PlainTextConsoleUtils {
8 |
9 | public static int arrayIndexOf(byte[] buf, int start, int end, byte[] matches) {
10 | int e = end - matches.length + 1;
11 |
12 | OUTER:
13 | for (int i = start; i < e; i++) {
14 | if (buf[i] == matches[0]) {
15 | // check for the rest of the match
16 | for (int j = 1; j < matches.length; j++) {
17 | if (buf[i + j] != matches[j])
18 | continue OUTER;
19 | }
20 | return i; // found it
21 | }
22 | }
23 | return -1; // not found
24 | }
25 |
26 | /**
27 | * the logical extracted from PlainTextConsoleOutputStream
28 | * console annotation will be removed, e.g.
29 | * Input:Started by user ESC[8mha:AAAAlh+LCAAAAAAAAP9b85aBtbiIQTGjNKU4P08vOT+vOD8nVc83PyU1x6OyILUoJzMv2y+/JJUBAhiZGBgqihhk0NSjKDWzXb3RdlLBUSYGJk8GtpzUvPSSDB8G5tKinBIGIZ+sxLJE/ZzEvHT94JKizLx0a6BxUmjGOUNodHsLgAzOEgYu/dLi1CL9vNKcHACFIKlWvwAAAA==ESC[0manonymous
30 | * Output:Started by user anonymous
31 | *
32 | * @param in the byte array
33 | * @param length how many bytes we want to read in
34 | * @param out write max(length) to out
35 | * @see hudson.console.PlainTextConsoleOutputStream
36 | */
37 | public static void decodeConsole(byte[] in, int length, ByteArrayOutputStream out) {
38 | int next = arrayIndexOf(in, 0, length, ConsoleNote.PREAMBLE);
39 | // perform byte[]->char[] while figuring out the char positions of the BLOBs
40 | int written = 0;
41 | while (next >= 0) {
42 | //text prior to PREAMBLE
43 | if (next > written) {
44 | out.write(in, written, next - written);
45 | written = next;
46 | }
47 | int endPos = arrayIndexOf(in, next + ConsoleNote.PREAMBLE.length, length, ConsoleNote.POSTAMBLE);
48 | if (endPos < 0) {
49 | break;
50 | } else {
51 | //skips to POSTAMBLE
52 | written = endPos + ConsoleNote.POSTAMBLE.length;
53 | next = arrayIndexOf(in, written, length, ConsoleNote.PREAMBLE);
54 | }
55 | }
56 | // finish the remaining bytes->chars conversion
57 | out.write(in, written, length - written);
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/splunk-devops/src/test/java/com/splunk/splunkjenkins/listeners/LoggingQueueListenerTest.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.listeners;
2 |
3 | import com.splunk.splunkjenkins.BaseTest;
4 | import com.splunk.splunkjenkins.Constants;
5 | import hudson.model.FreeStyleProject;
6 | import hudson.model.Label;
7 | import hudson.model.Queue;
8 | import hudson.model.queue.QueueTaskFuture;
9 | import org.junit.Test;
10 |
11 | import java.io.IOException;
12 | import java.util.concurrent.ExecutionException;
13 | import java.util.concurrent.TimeUnit;
14 |
15 | import static com.splunk.splunkjenkins.SplunkConfigUtil.verifySplunkSearchResult;
16 | import static org.junit.Assert.assertEquals;
17 |
18 | public class LoggingQueueListenerTest extends BaseTest {
19 | private int waitInQueueTime = 5;
20 | private String jobName = "validate-queue-foo";
21 |
22 | @Test
23 | public void testQueueMessage() throws ExecutionException, InterruptedException, IOException {
24 | FreeStyleProject project = j.createFreeStyleProject(jobName);
25 | long startTime = System.currentTimeMillis();
26 | // set a quiet period
27 | QueueTaskFuture future = project.scheduleBuild2(waitInQueueTime);
28 | Queue.Item[] items = j.jenkins.getQueue().getItems();
29 | assertEquals(1, items.length);
30 | Long queueId = items[0].getId();
31 | future.waitForStart();
32 | // check wait message
33 | String splQuery = "event_tag=queue queue_id=" + queueId + " type=dequeue_waiting "
34 | + "waiting_time>" + waitInQueueTime;
35 | verifySplunkSearchResult(splQuery, startTime, 1);
36 | splQuery = "event_tag=queue queue_id=" + queueId + " type=" + Constants.DEQUEUE_TAG_NAME;
37 | verifySplunkSearchResult(splQuery, startTime, 1);
38 | // set a label
39 | String nodeLabel = "remotes-no-such-node";
40 | project.setAssignedLabel(Label.get(nodeLabel));
41 | project.scheduleBuild2(0);
42 | items = j.jenkins.getQueue().getItems();
43 | assertEquals(1, items.length);
44 | queueId = items[0].getId();
45 | // job waits for start since there is no node have the label
46 | Thread.sleep(TimeUnit.SECONDS.toMillis(15));
47 | j.jenkins.getQueue().clear();
48 | splQuery = "event_tag=queue queue_id=" + queueId + " type=dequeue_buildable "
49 | + "buildable_time>3";
50 | verifySplunkSearchResult(splQuery, startTime, 1);
51 | splQuery = "event_tag=queue queue_id=" + queueId + " type=" + Constants.DEQUEUE_TAG_NAME;
52 | verifySplunkSearchResult(splQuery, startTime, 1);
53 | }
54 | }
--------------------------------------------------------------------------------
/splunk-devops-extend/src/main/java/com/splunk/splunkjenkins/console/LabelConsoleLineStream.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.console;
2 |
3 | import com.splunk.splunkjenkins.model.EventRecord;
4 | import hudson.util.ByteArrayOutputStream2;
5 |
6 | import java.io.FilterOutputStream;
7 | import java.io.IOException;
8 | import java.io.OutputStream;
9 | import java.util.logging.Level;
10 | import java.util.logging.Logger;
11 | import java.util.regex.Pattern;
12 |
13 | import org.apache.commons.lang3.StringUtils;
14 |
15 | import static com.splunk.splunkjenkins.Constants.CONSOLE_TEXT_SINGLE_LINE_MAX_LENGTH;
16 | import static com.splunk.splunkjenkins.model.EventType.CONSOLE_LOG;
17 |
18 | public class LabelConsoleLineStream extends FilterOutputStream {
19 | private static final int RECEIVE_BUFFER_SIZE = 512;
20 | private static final Logger LOGGER = Logger.getLogger(LabelConsoleLineStream.class.getName());
21 | public static final Pattern ANSI_COLOR_ESCAPE = Pattern.compile("\u001B\\[[\\d;]+m");
22 | private ByteArrayOutputStream2 branch = new ByteArrayOutputStream2(RECEIVE_BUFFER_SIZE);
23 | PipelineConsoleDecoder decoder;
24 | String source;
25 |
26 | public LabelConsoleLineStream(OutputStream out, String source, PipelineConsoleDecoder decoder) {
27 | super(out);
28 | this.decoder = decoder;
29 | this.source = source;
30 | }
31 |
32 | @Override
33 | public void write(int b) throws IOException {
34 | super.write(b);
35 | if (b == '\n') {
36 | eol();
37 | } else {
38 | //do not need write \n
39 | branch.write(b);
40 | }
41 | if (branch.size() > CONSOLE_TEXT_SINGLE_LINE_MAX_LENGTH) {
42 | eol();
43 | }
44 | }
45 |
46 | protected void eol() {
47 | String line = decoder.decodeLine(branch.getBuffer(), branch.size());
48 | if (line == null) {
49 | // actually line can not be null, always ends with \n, add null check in case decode error
50 | return;
51 | }
52 | // reuse the buffer under normal circumstances
53 | branch.reset();
54 | line = ANSI_COLOR_ESCAPE.matcher(line).replaceAll("");
55 | if (StringUtils.isNotBlank(line)) {
56 | EventRecord record = new EventRecord(line, CONSOLE_LOG);
57 | record.setSource(source);
58 | ConsoleRecordCacheUtils.enqueue(record);
59 | }
60 | }
61 |
62 | @Override
63 | public void flush() throws IOException {
64 | super.flush();
65 | ConsoleRecordCacheUtils.flushLog();
66 | LOGGER.log(Level.FINE, "flush splunk log for " + source);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/splunk-devops/src/test/java/com/splunk/splunkjenkins/TeeConsoleLogFilterTest.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins;
2 |
3 | import com.splunk.splunkjenkins.utils.LogEventHelper;
4 | import com.splunk.splunkjenkins.utils.SplunkLogService;
5 | import hudson.console.ConsoleAnnotationOutputStream;
6 | import hudson.model.FreeStyleBuild;
7 | import hudson.model.FreeStyleProject;
8 | import hudson.tasks.Shell;
9 |
10 | import java.io.IOException;
11 | import java.io.StringReader;
12 | import java.io.StringWriter;
13 | import java.nio.charset.StandardCharsets;
14 | import java.util.UUID;
15 | import java.util.concurrent.TimeUnit;
16 | import java.util.logging.Logger;
17 |
18 | import hudson.util.ByteArrayOutputStream2;
19 | import org.apache.commons.io.IOUtils;
20 | import org.junit.After;
21 | import org.junit.Before;
22 | import org.junit.Rule;
23 | import org.junit.Test;
24 | import org.jvnet.hudson.test.CaptureEnvironmentBuilder;
25 | import org.jvnet.hudson.test.JenkinsRule;
26 |
27 | import static com.splunk.splunkjenkins.SplunkConfigUtil.checkTokenAvailable;
28 | import static com.splunk.splunkjenkins.SplunkConfigUtil.verifySplunkSearchResult;
29 | import static org.junit.Assert.assertEquals;
30 | import static org.junit.Assert.assertFalse;
31 | import static org.junit.Assert.fail;
32 |
33 | public class TeeConsoleLogFilterTest extends BaseTest {
34 | private static final Logger LOG = Logger.getLogger(TeeConsoleLogFilterTest.class.getName());
35 |
36 | @Test
37 | public void decorateLogger() throws Exception {
38 | FreeStyleProject p = j.createFreeStyleProject("console_" + UUID.randomUUID());
39 | CaptureEnvironmentBuilder captureEnvironment = new CaptureEnvironmentBuilder();
40 | p.getBuildersList().add(captureEnvironment);
41 | p.getBuildersList().add(new Shell("echo $PATH;echo $$"));
42 | long eventCount = SplunkLogService.getInstance().getSentCount();
43 | FreeStyleBuild b = j.buildAndAssertSuccess(p);
44 | long timeToWait = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(2);
45 | while (eventCount >= SplunkLogService.getInstance().getSentCount() && (p.getLastBuild() == null || p.getLastBuild().isBuilding())) {
46 | Thread.sleep(1000);
47 | if (System.currentTimeMillis() > timeToWait) {
48 | LOG.fine("queue size:" + SplunkLogService.getInstance().getQueueSize());
49 | fail("can not send event in time");
50 | }
51 | }
52 | String query = "index=" + SplunkConfigUtil.INDEX_NAME + " source=" + b.getUrl() + "console";
53 | int expected = 5;
54 | verifySplunkSearchResult(query, b.getTimeInMillis(), expected);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/Messages.properties:
--------------------------------------------------------------------------------
1 | # The MIT License
2 | #
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy
4 | # of this software and associated documentation files (the "Software"), to deal
5 | # in the Software without restriction, including without limitation the rights
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | # copies of the Software, and to permit persons to whom the Software is
8 | # furnished to do so, subject to the following conditions:
9 | #
10 | # The above copyright notice and this permission notice shall be included in
11 | # all copies or substantial portions of the Software.
12 | #
13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | # THE SOFTWARE.
20 | #see also http://docs.oracle.com/javase/6/docs/api/java/util/Properties.html
21 | DisplayName=Splunk-Jenkins
22 | Description=A Splunk-Jenkins plugin for Splunking Jenkins.
23 | PleaseProvideHost=Please provide the hostname to a Splunk instance.
24 | CloudHostPrefix=You are using Splunk Cloud, please provide host name starts input- or http-inputs-, \
25 | please try input-{0} or http-inputs-{0}. See also http://dev.splunk.com/view/event-collector/SP-CAAAE7G
26 | HostNameSchemaWarning=Invalid hostname, do you mean {0}?
27 | HostNameListWarning=Multiple hosts is not supported, please setup load balancer, and user the virtual name
28 | HostNameInvalid=Failed to validate hostname
29 | ValueIntErrorMsg=This value must be a valid int.
30 | ValueCannotBeBlank=This value cannot be blank.
31 | ConfigBuildStepTitle=Send data to Splunk
32 | ConfigBuildStepSendLog=Send build log
33 | ConfigBuildStepSendEnvVars=Send environment variables
34 | ConfigBuildStepSendFiles=Files to send as an event
35 | ConfigBuildFileToAppend=File to append to event
36 | InvalidToken=Token is invalid
37 | InvalidPattern=The pattern is invalid, please check Pattern
38 | InvalidHostOrToken=Invalid config, please check Hostname or Token
39 | SplunArtifactArchive=Send files to Splunk
40 | SplunkIconName=/plugin/splunk-devops/images/splunk_logo_green.png
41 | PermissionGroup=Splunk
42 | PluginViewPermission.Description=This permission grants access to the Splunk Instance Link.
43 |
--------------------------------------------------------------------------------
/splunk-devops/src/test/resources/com/splunk/splunkjenkins/TestResultAdapterTest/jobs/xunit_job1/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | false
6 |
7 |
8 | true
9 | false
10 | false
11 | false
12 |
13 | false
14 |
15 |
16 | cat >test-result.xml <<'EOF'
17 | <?xml version="1.0" encoding="UTF-8"?>
18 | <testsuite name="hudson.util.ProcessTreeTest" time="0.357" tests="3" errors="0" skipped="0" failures="0">
19 | <testcase name="remoting" classname="hudson.util.ProcessTreeTest" time="0.157"/>
20 | <testcase name="remoting-a" classname="hudson.util.ProcessTreeTest" time="0.157"/>
21 | <testcase name="remoting-b" classname="hudson.util.ProcessTreeTest" time="0.157"/>
22 |
23 | </testsuite>
24 | EOF
25 |
26 |
27 |
28 |
29 |
30 |
31 | test-result.xml
32 | true
33 | true
34 | true
35 | true
36 |
37 |
38 |
39 |
40 |
41 | 1
42 |
43 | 2
44 |
45 |
46 |
47 | 1
48 |
49 | 2
50 |
51 |
52 | 1
53 |
54 | 3000
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/WebPostAccessLogger.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins;
2 |
3 | import com.splunk.splunkjenkins.utils.SplunkLogService;
4 | import jenkins.model.Jenkins;
5 | import org.acegisecurity.Authentication;
6 |
7 | import javax.servlet.Filter;
8 | import javax.servlet.FilterChain;
9 | import javax.servlet.FilterConfig;
10 | import javax.servlet.ServletException;
11 | import javax.servlet.ServletRequest;
12 | import javax.servlet.ServletResponse;
13 | import javax.servlet.http.HttpServletRequest;
14 | import java.io.IOException;
15 | import java.util.HashMap;
16 | import java.util.Map;
17 | import java.util.logging.Logger;
18 | import java.util.regex.Pattern;
19 |
20 | import static com.splunk.splunkjenkins.Constants.TAG;
21 | import static com.splunk.splunkjenkins.model.EventType.JENKINS_CONFIG;
22 |
23 | public class WebPostAccessLogger implements Filter {
24 | private static final Logger LOG = Logger.getLogger(WebPostAccessLogger.class.getName());
25 | private static final Pattern FILTER_PATTERN = Pattern.compile("/(?:configSubmit|updateSubmit|script|doDelete)");
26 |
27 | @Override
28 | public void init(FilterConfig filterConfig) throws ServletException {
29 | LOG.info("splunk-filter loaded");
30 | }
31 |
32 | @Override
33 | public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
34 | try {
35 | loggerUserAction(servletRequest);
36 | } catch (Exception e) {
37 | //ignore;
38 | }
39 | filterChain.doFilter(servletRequest, servletResponse);
40 | }
41 |
42 | private void loggerUserAction(ServletRequest servletRequest) {
43 | if (SplunkJenkinsInstallation.get().isEventDisabled(JENKINS_CONFIG)) {
44 | return;
45 | }
46 | if (!(servletRequest instanceof HttpServletRequest)) {
47 | return;
48 | }
49 | HttpServletRequest request = (HttpServletRequest) servletRequest;
50 | if (!"POST".equals(request.getMethod())) {
51 | return;
52 | }
53 | Authentication auth = Jenkins.getAuthentication();
54 | String path = request.getRequestURI();
55 | if (auth == null || path == null || !auth.isAuthenticated()) {
56 | return;
57 | }
58 | if (!FILTER_PATTERN.matcher(path).find()) {
59 | return;
60 | }
61 | Map auditInfo = new HashMap();
62 | auditInfo.put("user", auth.getName());
63 | auditInfo.put("message", "POST " + request.getRequestURI());
64 | auditInfo.put(TAG, "audit_trail");
65 | SplunkLogService.getInstance().send(auditInfo, JENKINS_CONFIG, "web_access");
66 | }
67 |
68 | @Override
69 | public void destroy() {
70 |
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | Splunk for Jenkins
2 | ---------
3 |
4 | To Install Develop Version
5 | ----
6 | - clone the repo
7 | - `mvn package`
8 | - mvn will generate `splunk-devops/target/splunk-devops.hpi` which you can install into Jenkins by the web interface or just put it in the `JENKINS_HOME/plugins` folder
9 |
10 |
11 | To Setup
12 | ----
13 | ### Configure plugin
14 |
15 | - Go to https://jenkins-url/configure
16 | - Enter Hostname, Port, and Token
17 | - Enable RawEvent support if you are using Splunk version 6.3.1511 or later
18 | - Click "Test Connection" to verify the config
19 | - Enable it and Save
20 |
21 | 
22 |
23 | ### Customize Job Data Sent to Splunk
24 |
25 | - In the advance configure section, you can customize the post data using groovy DSL
26 | - ``send(Object message)`` will send the information to splunk
27 | - ``AbstractBuild build``, ``Map env`` can be used directly. Variable env is a Map of Environment variables, build is hudson.model.AbstractBuild
28 | - `getBuildEvent()` will return metadata about the build, such as build result, build URL, user who triggered the build
29 | - `getJunitReport(int pageSize)` will return a list of test results, which contains total, passes, failures, skips, time and testcase of type List
30 | - `getJunitReport()` is an alias of `getJunitReport(Integer.MAX_VALUE)[0]`
31 | - sendCoverageReport(pageSize) send coverage report, with pagination support
32 | - sendTestReport(pageSize) send Test report, with pagination support
33 | - `archive(String includes, String excludes, boolean uploadFromSlave, String fileSizeLimit)` send log file to splunk
34 | - `archive(String includes)` is an alias of `archive(includes, null, false, "")`
35 | - `getAction(Class type)` is an alias of ` build.getAction(type)`
36 | - `getActionByClassName(String className)` same as `getAction(Class type)` but no need to import the class before use
37 | - `hasPublisherName(String className)` check whether the publisher is configured for the build (applied to AbstractBuild only)
38 | - Here is the default settings for post job data processing
39 |
40 | ```groovy
41 | //send job metadata and junit reports with page size set to 50 (each event contains max 50 test cases)
42 | splunkins.sendTestReport(50)
43 | //send coverage, each event contains max 50 class metrics
44 | splunkins.sendCoverageReport(50)
45 | //send all logs from workspace to splunk, with each file size limits to 10MB
46 | splunkins.archive("**/*.log", null, false, "10MB")
47 |
48 | ```
49 |
50 | Contributing to the plugin
51 | ----
52 | - clone the repo and update code
53 | - start splunk, you can get a free trail version from
54 | [Splunk](https://splunk.com/)
55 | - `mvn clean verify -Dsplunk-host=localhost -Dsplunk-username=admin -Dsplunk-passwd=changeme`
56 | to run tests using local splunk instance.
57 | - send pull requests
58 |
59 | Documentation
60 | ----
61 | See the [documentation](doc/splunk-devops-usage.md)
62 |
--------------------------------------------------------------------------------
/splunk-devops-extend/src/main/java/com/splunk/splunkjenkins/PipelineGraphVizSupport.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins;
2 |
3 | import com.splunk.splunkjenkins.model.EventType;
4 | import com.splunk.splunkjenkins.model.LoggingJobExtractor;
5 | import com.splunk.splunkjenkins.utils.SplunkLogService;
6 | import hudson.Extension;
7 | import org.jenkinsci.plugins.workflow.graph.BlockEndNode;
8 | import org.jenkinsci.plugins.workflow.graph.BlockStartNode;
9 | import org.jenkinsci.plugins.workflow.graph.FlowGraphWalker;
10 | import org.jenkinsci.plugins.workflow.graph.FlowNode;
11 | import org.jenkinsci.plugins.workflow.job.WorkflowRun;
12 |
13 | import java.util.Collections;
14 | import java.util.Map;
15 | import java.util.logging.Level;
16 | import java.util.logging.Logger;
17 |
18 | /**
19 | * The getDot code is borrowed from https://plugins.jenkins.io/workflow-job
20 | */
21 | @Extension
22 | public class PipelineGraphVizSupport extends LoggingJobExtractor {
23 | private static final String SUFFIX = "graphviz";
24 | private static final Logger LOGGER = Logger.getLogger(PipelineGraphVizSupport.class.getName());
25 |
26 | @Override
27 | public Map extract(WorkflowRun workflowRun, boolean completed) {
28 | if (!completed) {
29 | return Collections.EMPTY_MAP;
30 | }
31 | if (!SplunkJenkinsInstallation.get().isJobIgnored(workflowRun.getUrl())) {
32 | SplunkPipelineJobProperty jobProperty = workflowRun.getParent().getProperty(SplunkPipelineJobProperty.class);
33 | LOGGER.log(Level.FINE, "job {0}, property {1}", new Object[]{workflowRun.getUrl(), jobProperty});
34 | if (jobProperty != null && jobProperty.isDiagramEnabled()) {
35 | String dotStr = getDot(workflowRun);
36 | String source = workflowRun.getUrl() + SUFFIX;
37 | SplunkLogService.getInstance().send(dotStr, EventType.BUILD_EVENT, source);
38 | }
39 | }
40 | return Collections.EMPTY_MAP;
41 | }
42 |
43 |
44 | private String getDot(WorkflowRun run) {
45 | StringBuffer buffer = new StringBuffer();
46 | buffer.append("digraph G {\n");
47 | FlowGraphWalker walker = new FlowGraphWalker(run.getExecution());
48 | for (FlowNode n : walker) {
49 | for (FlowNode p : n.getParents()) {
50 | buffer.append(String.format("%s -> %s%n", p.getId(), n.getId()));
51 | }
52 |
53 | if (n instanceof BlockStartNode) {
54 | buffer.append(String.format("%s [shape=trapezium]%n", n.getId()));
55 | } else if (n instanceof BlockEndNode) {
56 | BlockEndNode sn = (BlockEndNode) n;
57 | buffer.append(String.format("%s [shape=invtrapezium]%n", n.getId()));
58 | buffer.append(String.format("%s -> %s [style=dotted]%n", sn.getStartNode().getId(), n.getId()));
59 | }
60 | buffer.append(String.format("%s [label=\"%s: %s\"]%n", n.getId(), n.getId(), n.getDisplayName()));
61 | }
62 |
63 | buffer.append("}");
64 | return buffer.toString();
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/groovy/com/splunk/splunkjenkins/UserActionDSL.groovy:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins
2 |
3 | import com.splunk.splunkjenkins.listeners.LoggingRunListener
4 | import com.splunk.splunkjenkins.utils.LogEventHelper
5 | import hudson.model.Run
6 | import hudson.model.TaskListener
7 | import jenkins.model.Jenkins
8 | import org.apache.commons.lang3.StringUtils
9 | import org.codehaus.groovy.control.CompilerConfiguration
10 | import org.codehaus.groovy.control.customizers.ImportCustomizer
11 | import org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval
12 | import org.jenkinsci.plugins.scriptsecurity.scripts.languages.GroovyLanguage
13 | import org.kohsuke.stapler.jelly.groovy.GroovyClosureScript
14 |
15 | import java.util.logging.Level
16 | import java.util.logging.Logger
17 |
18 | import static com.splunk.splunkjenkins.utils.LogEventHelper.getBuildVariables
19 |
20 | public class UserActionDSL {
21 | static final LOG = Logger.getLogger(LoggingRunListener.class.name)
22 |
23 | public void perform(Run build, TaskListener listener, GroovyCodeSource codeSource) {
24 | try {
25 | Map buildParameters = getBuildVariables(build);
26 | String scriptText=codeSource.getScriptText()
27 | if (StringUtils.isNotEmpty(scriptText)) {
28 | def workSpace;
29 | if (build.metaClass.respondsTo(build, "getWorkspace")) {
30 | //getWorkspace defined in build
31 | workSpace = build.workspace;
32 | }
33 | RunDelegate delegate = new RunDelegate(build: build, workSpace: workSpace,
34 | env: buildParameters, listener: listener);
35 | Binding binding = new Binding();
36 | binding.setVariable("splunkins", delegate);
37 | try {
38 | //check approval, will throw UnapprovedUsageException
39 | ScriptApproval.get().using(scriptText, GroovyLanguage.get())
40 | //call setDelegate to RunDelegate instance and run
41 | CompilerConfiguration cc = new CompilerConfiguration();
42 | cc.scriptBaseClass = GroovyClosureScript.class.name;
43 | ImportCustomizer ic = new ImportCustomizer()
44 | ic.addStaticStars(LogEventHelper.class.name)
45 | ic.addStarImport("jenkins.model")
46 | cc.addCompilationCustomizers(ic)
47 | GroovyClosureScript dslScript = (GroovyClosureScript) new GroovyShell(Jenkins.instance.pluginManager.uberClassLoader, binding, cc)
48 | .parse(codeSource)
49 | dslScript.setDelegate(delegate);
50 | dslScript.run()
51 | } catch (Exception e) {
52 | LOG.log(Level.SEVERE, "UserActionDSL script failed", e);
53 | e.printStackTrace(listener.getLogger())
54 | }
55 | listener.getLogger().flush();
56 | }
57 | } catch (Exception e) {
58 | e.printStackTrace();
59 | }
60 | }
61 |
62 | }
63 |
64 |
--------------------------------------------------------------------------------
/splunk-devops/src/test/java/com/splunk/splunkjenkins/SplunkArchiveFileTest.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins;
2 |
3 | import hudson.Functions;
4 | import hudson.model.FreeStyleBuild;
5 | import hudson.model.FreeStyleProject;
6 | import hudson.model.Label;
7 | import hudson.tasks.BatchFile;
8 | import hudson.tasks.Shell;
9 | import org.junit.Test;
10 | import org.jvnet.hudson.test.CaptureEnvironmentBuilder;
11 |
12 | import java.util.UUID;
13 | import java.util.logging.Logger;
14 |
15 | import static com.splunk.splunkjenkins.SplunkConfigUtil.INDEX_NAME;
16 | import static com.splunk.splunkjenkins.SplunkConfigUtil.verifySplunkSearchResult;
17 | import static org.junit.Assert.assertNotNull;
18 | import static org.junit.Assert.fail;
19 |
20 | public class SplunkArchiveFileTest extends BaseTest {
21 | public static Object result = null;
22 | private static final Logger logger = Logger.getLogger(SplunkArchiveFileTest.class.getName());
23 |
24 | public String buildWithScript(String groovyScript) throws Exception {
25 | Label label = j.jenkins.getLabel("filetest");
26 | j.createOnlineSlave(label);
27 | FreeStyleProject project = j.createFreeStyleProject("verify_archive" + UUID.randomUUID());
28 | CaptureEnvironmentBuilder captureEnvironment = new CaptureEnvironmentBuilder();
29 | project.getBuildersList().add(captureEnvironment);
30 | if(Functions.isWindows()) {
31 | project.getBuildersList().add(new BatchFile("echo file-test > process_list.txt"));
32 | } else {
33 | project.getBuildersList().add(new Shell("echo file-test > process_list.txt"));
34 | }
35 | project.setAssignedLabel(label);
36 | SplunkJenkinsInstallation.get().setScriptContent(groovyScript);
37 | SplunkJenkinsInstallation.get().updateCache();
38 | long start_time = System.currentTimeMillis();
39 | FreeStyleBuild build = j.buildAndAssertSuccess(project);
40 | String buildUrl = build.getUrl();
41 | int expected = 1;
42 | String query = "search index=" + INDEX_NAME
43 | + " source=\"" + buildUrl + "process_list.txt\"";
44 | logger.info(query);
45 | verifySplunkSearchResult(query, start_time, expected);
46 | return build.getUrl();
47 | }
48 |
49 | @Test
50 | public void testUploadFromSlave() {
51 | String script = "println \"uploading files\"\n" +
52 | "splunkins.archive(\"*.txt\",\"\",true,\"0\")";
53 | try {
54 | buildWithScript(script);
55 | } catch (Exception e) {
56 | e.printStackTrace();
57 | fail("upload failed");
58 | }
59 | }
60 |
61 | @Test
62 | public void testUploadFromMaster() throws Exception {
63 | result = null;
64 | String script = "println \"uploading files\"\n" +
65 | "def sentCount=splunkins.archive(\"*.txt\",\"\",false,\"10MB\");" +
66 | "com.splunk.splunkjenkins.SplunkArchiveFileTest.result=sentCount;" +
67 | "println \"send \"+sentCount";
68 | buildWithScript(script);
69 | assertNotNull("archive file completed", result);
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/splunk-devops/src/test/java/com/splunk/splunkjenkins/utils/TestCaseResultUtilsTest.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.utils;
2 |
3 | import com.splunk.splunkjenkins.BaseTest;
4 | import com.splunk.splunkjenkins.Constants;
5 | import com.splunk.splunkjenkins.model.AbstractTestResultAdapter;
6 | import com.splunk.splunkjenkins.model.JunitResultAdapter;
7 | import com.splunk.splunkjenkins.model.JunitTestCaseGroup;
8 | import hudson.model.Run;
9 | import hudson.tasks.junit.TestResult;
10 | import hudson.tasks.junit.TestResultAction;
11 | import org.junit.Test;
12 |
13 | import java.io.File;
14 | import java.io.FileWriter;
15 | import java.util.List;
16 |
17 | import static org.junit.Assert.assertEquals;
18 | import static org.junit.Assert.assertTrue;
19 |
20 | public class TestCaseResultUtilsTest extends BaseTest {
21 | public void splitJunitTestCase(int total, int pageSize) throws Exception {
22 | TestResult result = new TestResult();
23 | File junitFile = File.createTempFile("junit", ".xml");
24 | FileWriter out = new FileWriter(junitFile);
25 | out.write("\n");
26 | out.write("\n" +
27 | "\t\n" +
28 | "\t \n" +
29 | " ");
30 | for (int i = 1; i < total; i++) {
31 | out.write("")
33 | .concat(" \n"));
34 | }
35 | out.write(" ");
36 | out.close();
37 | result.parse(junitFile);
38 | result.tally();
39 | assertEquals(total, result.getTotalCount());
40 | assertEquals(1, result.getFailCount());
41 |
42 | TestResultAction action = new TestResultAction((Run) null, result, null);
43 | JunitResultAdapter adapter = new JunitResultAdapter();
44 | List suites = TestCaseResultUtils.split(adapter.getTestResult(action)
45 | , pageSize);
46 | int remained = (total % pageSize == 0) ? 0 : 1;
47 | int pageCount = total / pageSize + remained;
48 | assertEquals(pageCount, suites.size());
49 | }
50 |
51 | @Test
52 | public void testSplitReminder() throws Exception {
53 | splitJunitTestCase(512, 5);
54 | }
55 |
56 | @Test
57 | public void testSplitDivide() throws Exception {
58 | splitJunitTestCase(5, 5);
59 | }
60 |
61 | @Test
62 | public void testTrimStdout() {
63 | int messageSize = (1 << 21) + 1;
64 | String originalMessage = new String(new byte[messageSize]);
65 | String truncatedMessage = AbstractTestResultAdapter.trimToLimit(originalMessage, "case1", "job/1");
66 | assertEquals(Constants.MAX_JUNIT_STDIO_SIZE, truncatedMessage.length());
67 | //short one
68 | messageSize = (1 << 21) - 1;
69 | originalMessage = new String(new byte[messageSize]);
70 | truncatedMessage = AbstractTestResultAdapter.trimToLimit(originalMessage, "case1", "job/1");
71 | assertTrue(truncatedMessage.length() == messageSize);
72 | }
73 | }
--------------------------------------------------------------------------------
/splunk-devops-extend/src/main/java/com/splunk/splunkjenkins/console/PipelineConsoleDecoder.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.console;
2 |
3 | import com.splunk.splunkjenkins.Constants;
4 | import com.splunk.splunkjenkins.utils.PlainTextConsoleUtils;
5 | import hudson.console.ConsoleNote;
6 | import org.jenkinsci.plugins.workflow.job.WorkflowRun;
7 |
8 | import edu.umd.cs.findbugs.annotations.CheckForNull;
9 | import java.io.ByteArrayInputStream;
10 | import java.io.ByteArrayOutputStream;
11 | import java.io.DataInputStream;
12 | import java.io.IOException;
13 | import java.io.Serializable;
14 | import java.util.logging.Logger;
15 |
16 | import static com.splunk.splunkjenkins.utils.PlainTextConsoleUtils.arrayIndexOf;
17 | import static java.util.logging.Level.WARNING;
18 |
19 | public class PipelineConsoleDecoder implements Serializable {
20 | private static final long serialVersionUID = 1L;
21 | private static final Logger LOG = Logger.getLogger(PipelineConsoleDecoder.class.getName());
22 | private transient WorkflowRun run;
23 | private transient LabelMarkupText markupText = new LabelMarkupText();
24 | private boolean parseLabelFlag = Constants.DECODE_PIPELINE_CONSOLE;
25 |
26 | public PipelineConsoleDecoder(WorkflowRun run) {
27 | this.run = run;
28 | if (run == null) {
29 | parseLabelFlag = false;
30 | }
31 | }
32 |
33 | @CheckForNull
34 | public String decodeLine(byte[] in, int length) {
35 | try {
36 | ByteArrayOutputStream bout = new ByteArrayOutputStream();
37 | if (parseLabelFlag) {
38 | decodeConsoleObjectStream(in, length, bout);
39 | } else {
40 | PlainTextConsoleUtils.decodeConsole(in, length, bout);
41 | }
42 | return bout.toString("UTF-8");
43 | } catch (IOException ex) {
44 | LOG.log(WARNING, "failed to decode log" + ex);
45 | return null;
46 | }
47 | }
48 |
49 | private void decodeConsoleObjectStream(byte[] in, int length, ByteArrayOutputStream out) throws IOException {
50 | int next = arrayIndexOf(in, 0, length, ConsoleNote.PREAMBLE);
51 | // perform byte[]->char[] while figuring out the char positions of the BLOBs
52 | int written = 0;
53 | while (next >= 0) {
54 | if (next > written) {
55 | out.write(in, written, next - written);
56 | written = next;
57 | }
58 | int rest = length - next;
59 | ByteArrayInputStream b = new ByteArrayInputStream(in, next, rest);
60 | try {
61 | ConsoleNote consoleNote = ConsoleNote.readFrom(new DataInputStream(b));
62 | consoleNote.annotate(run, markupText, 0);
63 | markupText.write(out);
64 | } catch (IOException | ClassNotFoundException ex) {
65 | LOG.log(WARNING, "failed to decode console note", ex);
66 | }
67 | int bytesUsed = rest - b.available(); // bytes consumed by annotations
68 | written += bytesUsed;
69 | next = arrayIndexOf(in, written, length, ConsoleNote.PREAMBLE);
70 | }
71 | if (length - written > 0) {
72 | markupText.writePreviousLabel(out);
73 | // finish the remaining bytes->chars conversion
74 | out.write(in, written, length - written);
75 | }
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/splunk-devops/src/test/java/com/splunk/splunkjenkins/SplunkLogServiceTest.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins;
2 |
3 | import java.io.IOException;
4 | import java.util.HashMap;
5 | import java.util.Map;
6 | import java.util.UUID;
7 | import java.util.concurrent.TimeUnit;
8 | import java.util.logging.Logger;
9 |
10 | import com.splunk.splunkjenkins.model.EventType;
11 | import com.splunk.splunkjenkins.utils.SplunkLogService;
12 | import org.junit.*;
13 |
14 | import static com.splunk.splunkjenkins.SplunkConfigUtil.verifySplunkSearchResult;
15 | import static org.junit.Assert.*;
16 |
17 | public class SplunkLogServiceTest extends BaseTest {
18 | private static final Logger LOG = Logger.getLogger(SplunkLogServiceTest.class.getName());
19 | private static final int BATCH_COUNT = 1000;
20 |
21 | /**
22 | * Test of update method, of class SplunkLogService.
23 | */
24 | @Test
25 | public void testLogServiceSendMethod() throws IOException, InterruptedException {
26 | LOG.info("running test SplunkLogServiceTest testLogServiceSendMethod");
27 | assertTrue("config should be valid", SplunkJenkinsInstallation.get().isValid());
28 | String line = "127.0.0.1 - admin \"GET /en-US/ HTTP/1.1\"";
29 | boolean queuedGenericMessage = SplunkLogService.getInstance().send(line);
30 | assertTrue("should put message in queue", queuedGenericMessage);
31 | long timestamp = System.currentTimeMillis();
32 | String query = "index=" + SplunkConfigUtil.INDEX_NAME + " |spath batch|where batch=" + timestamp;
33 | LOG.info(query);
34 | long initNumber = SplunkLogService.getInstance().getSentCount();
35 | for (int i = 0; i < BATCH_COUNT; i++) {
36 | Map data = new HashMap();
37 | data.put("id", UUID.randomUUID().toString());
38 | data.put("batch", timestamp);
39 | data.put("number", i);
40 | boolean queued = SplunkLogService.getInstance().send(data);
41 | assertTrue("should put the message to queue", queued);
42 | }
43 | //give some time to send,max wait time is 3 minute
44 | long timeToWait = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(3);
45 | while (SplunkLogService.getInstance().getSentCount() < (BATCH_COUNT + initNumber)) {
46 | Thread.sleep(1000);
47 | long queueSize = SplunkLogService.getInstance().getQueueSize();
48 | long sentCount = SplunkLogService.getInstance().getSentCount();
49 | long remaining = BATCH_COUNT + initNumber - sentCount;
50 | LOG.fine("queue size:" + queueSize + " sent:" + sentCount);
51 | if (System.currentTimeMillis() > timeToWait) {
52 | fail("can not send events in time, remaining " + remaining);
53 | }
54 | }
55 | int expected = BATCH_COUNT;
56 | verifySplunkSearchResult(query, timestamp, expected);
57 | }
58 |
59 | @Test
60 | public void sendFloatNaN() {
61 | Map result = new HashMap();
62 | result.put("floatNaN", Float.NaN);
63 | result.put("doubleMaxVal", Double.MAX_VALUE);
64 | result.put("doubleMinVal", Double.MIN_VALUE);
65 | result.put("doubleNaN", Double.NaN);
66 | long timestamp = System.currentTimeMillis();
67 | SplunkLogService.getInstance().send(result, EventType.LOG);
68 | String query = "doubleMaxVal>9999999999999999";
69 | verifySplunkSearchResult(query, timestamp, 1);
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/splunk-devops-extend/src/main/java/com/splunk/splunkjenkins/console/SplunkTaskListenerFactory.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.console;
2 |
3 | import com.google.common.cache.CacheBuilder;
4 | import com.google.common.cache.CacheLoader;
5 | import com.google.common.cache.LoadingCache;
6 | import com.splunk.splunkjenkins.SplunkJenkinsInstallation;
7 | import hudson.Extension;
8 | import hudson.model.Queue;
9 | import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner;
10 | import org.jenkinsci.plugins.workflow.job.WorkflowRun;
11 | import org.jenkinsci.plugins.workflow.log.TaskListenerDecorator;
12 |
13 | import edu.umd.cs.findbugs.annotations.CheckForNull;
14 | import edu.umd.cs.findbugs.annotations.NonNull;
15 |
16 | import java.io.IOException;
17 | import java.util.concurrent.ExecutionException;
18 | import java.util.logging.Level;
19 | import java.util.logging.Logger;
20 |
21 | import static com.splunk.splunkjenkins.model.EventType.CONSOLE_LOG;
22 |
23 | @Extension(optional = true)
24 | public class SplunkTaskListenerFactory implements TaskListenerDecorator.Factory {
25 | private static final Logger LOGGER = Logger.getLogger(SplunkConsoleTaskListenerDecorator.class.getName());
26 | private static final boolean ENABLE_REMOTE_DECORATOR = Boolean.parseBoolean(System.getProperty("splunkins.enableRemoteTaskListenerDecorator", "true"));
27 | private static final transient LoadingCache cachedDecorator = CacheBuilder.newBuilder()
28 | .weakKeys()
29 | .maximumSize(1024)
30 | .build(new CacheLoader() {
31 | @Override
32 | public SplunkConsoleTaskListenerDecorator load(WorkflowRun key) {
33 | SplunkConsoleTaskListenerDecorator decorator = new SplunkConsoleTaskListenerDecorator(key);
34 | if (ENABLE_REMOTE_DECORATOR) {
35 | decorator.setRemoteSplunkinsConfig(SplunkJenkinsInstallation.get().toMap());
36 | }
37 | return decorator;
38 | }
39 | });
40 |
41 | @Override
42 | /*
43 | data stream is passed to splunk decorator first (it sees data in the last due to decorator behavior)
44 | */
45 | public boolean isAppliedBeforeMainDecorator() {
46 | return true;
47 | }
48 |
49 | @CheckForNull
50 | @Override
51 | public TaskListenerDecorator of(@NonNull FlowExecutionOwner flowExecutionOwner) {
52 | if (!SplunkJenkinsInstallation.get().isPipelineFilterEnabled()) {
53 | return null;
54 | }
55 | if (SplunkJenkinsInstallation.get().isEventDisabled(CONSOLE_LOG)) {
56 | return null;
57 | }
58 | try {
59 | Queue.Executable executable = flowExecutionOwner.getExecutable();
60 | if (executable instanceof WorkflowRun) {
61 | WorkflowRun run = (WorkflowRun) executable;
62 | if (SplunkJenkinsInstallation.get().isJobIgnored(run.getUrl())) {
63 | return null;
64 | }
65 | return cachedDecorator.get(run);
66 | }
67 | } catch (IOException x) {
68 | LOGGER.log(Level.WARNING, null, x);
69 | } catch (ExecutionException e) {
70 | LOGGER.finer("failed to load cached decorator");
71 | }
72 | return null;
73 | }
74 |
75 | public static void removeCache(WorkflowRun run) {
76 | cachedDecorator.invalidate(run);
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/doc/extension.md:
--------------------------------------------------------------------------------
1 | Plugin implements some interfaces and marks the implementation to use annotation @Extension so Jenkins can load it dynamically
2 |
3 | # Configure
4 |
5 | ### SplunkJenkinsInstallation
6 | package com.splunk.splunkjenkins
7 | Follow jenkins conventions, using java bean property host, port, token, useSSL, scriptPath etc and some helper method such as
8 | doCheckHost to validate host, doTestHttpInput to verify connection, see also https://wiki.jenkins-ci. org/display/JENKINS/Form+Validation
9 |
10 | # Function
11 | ### Listeners
12 | package com.splunk.splunkjenkins.listeners, receives notifications from jenkins, for example in Jenkins delete job action
13 | ```
14 | /**
15 | * Called in response to {@link Job#doDoDelete(StaplerRequest, StaplerResponse)}
16 | */
17 | public void onDeleted(TopLevelItem item) throws IOException {
18 | ItemListener.fireOnDeleted(item);
19 |
20 | items.remove(item.getName());
21 | // For compatibility with old views:
22 | for (View v : views)
23 | v.onJobRenamed(item, item.getName(), null);
24 | }
25 |
26 | ```
27 | it will call fireOnDeleted to notify all ItemListeners
28 |
29 |
30 | ## Listeners list
31 | #### [SecurityListener](http://javadoc.jenkins-ci.org/jenkins/security/SecurityListener.html)
32 | record user login/logout and failedToLogIn events
33 | #### [SaveableListener](http://javadoc.jenkins-ci.org/hudson/model/listeners/SaveableListener.html)
34 | when user made changes to jenkins config (either plugin config or job config), record the config xml. disabled by default until user add jenkins_config.monitoring=true into metadata config
35 | #### [ItemListener](http://javadoc.jenkins-ci.org/hudson/model/listeners/RunListener.html)
36 | similar to SaveableListener but for Job only, it has finer grained audit event. used to capture job created, renamed, copied and deleted
37 | #### [QueueListener](http://javadoc.jenkins-ci.org/hudson/model/queue/QueueListener.html)
38 | listen for onEnterWaiting, onLeaveWaiting, onEnterBlocked, onLeaveBlocked, onEnterBuildable, onLeavebBuildable, and onLeft. Record the job queueTime in each of the phase along with other jenkins metrics and reports to Splunk.
39 | #### [RunListener](http://javadoc.jenkins-ci.org/hudson/model/listeners/RunListener.html)
40 | listen for onStarted and onCompleted, and extract upstream job, build cause, scm, job result, and invoke DSL if defined
41 | #### [ComputerListener](http://javadoc.jenkins-ci.org/hudson/slaves/ComputerListener.html)
42 | record slave online, offline, temporarilyOffline, and launchFailure
43 | ## [Notifier](http://javadoc.jenkins-ci.org/hudson/tasks/Notifier.html)
44 | Contribute to post build step, user can custom the logs to send to splunk in addition to what we have in Groovy DSL.
45 |
46 | ## Links
47 | ### RootAction
48 | add link to home page
49 | ### ManagementLink
50 | add link to management page
51 | ### TransientComputerActionFactory
52 | and link to other types, such as Build, Computer
53 |
54 | ## Task
55 | we extends AsyncPeriodicWork to run tasks periodic to gather agent metrics
56 |
57 | # Service
58 | SplunkLogService launched two threads to drain the splunk event queue, used (Splunk HTTP Event Collector)[http://dev.splunk.com/view/event-collector/SP-CAAAE6M] to pump data into splunk, message format
59 |
60 | ```
61 |
62 | {
63 | "time": 1426279439,
64 | "host": "localhost",
65 | "source": "datasource",
66 | "sourcetype": "txt",
67 | "index": "main",
68 | "event": { jenkins_event_json_here }
69 | }
70 |
71 | ```
72 |
73 |
--------------------------------------------------------------------------------
/splunk-devops-shaded/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 |
6 | com.splunk.splunkins
7 | pom
8 | 1.11.2-SNAPSHOT
9 |
10 |
11 | splunk-devops-shaded
12 | jar
13 |
14 | Shaded http client and gson jar
15 |
16 |
17 |
18 |
19 | org.apache.maven.plugins
20 | maven-shade-plugin
21 | 3.3.0
22 |
23 |
24 | package
25 |
26 | shade
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | true
35 |
36 |
37 | org.apache.httpcomponents:*
38 | com.google.code.gson:gson
39 |
40 |
41 |
42 |
43 | org.apache.http
44 | shaded.splk.org.apache.http
45 |
46 |
47 | com.google.gson
48 | shaded.splk.com.google.gson
49 |
50 |
51 | true
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | org.apache.httpcomponents
63 | httpcore
64 | 4.4.13
65 |
66 |
67 | org.apache.httpcomponents
68 | httpclient
69 | 4.5.13
70 |
71 |
72 | com.google.code.gson
73 | gson
74 | 2.8.9
75 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/splunk-devops/src/test/resources/com/splunk/splunkjenkins/CoverageMetricTest/jobs/Clover/workspace/clover.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/splunk-devops/src/test/java/com/splunk/splunkjenkins/SplunkJenkinsInstallationTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License
3 | *
4 | * Copyright 2023 CloudBees, Inc.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package com.splunk.splunkjenkins;
26 |
27 | import com.splunk.splunkjenkins.model.EventType;
28 | import com.splunk.splunkjenkins.model.MetaDataConfigItem;
29 |
30 | import java.io.File;
31 | import java.nio.charset.StandardCharsets;
32 | import java.util.Collections;
33 | import java.util.Map;
34 | import java.util.Set;
35 |
36 | import com.splunk.splunkjenkins.utils.RemoteUtils;
37 | import hudson.util.Secret;
38 | import jenkins.model.Jenkins;
39 | import org.apache.commons.io.FileUtils;
40 |
41 | import static org.hamcrest.MatcherAssert.assertThat;
42 | import static org.hamcrest.Matchers.containsString;
43 | import static org.hamcrest.Matchers.hasSize;
44 | import static org.hamcrest.Matchers.is;
45 |
46 | import org.junit.Rule;
47 | import org.junit.Test;
48 | import org.jvnet.hudson.test.JenkinsRule;
49 |
50 | public final class SplunkJenkinsInstallationTest {
51 |
52 | @Rule
53 | public final JenkinsRule r = new JenkinsRule();
54 |
55 | @Test
56 | public void reload() throws Exception {
57 | SplunkJenkinsInstallation cfg = SplunkJenkinsInstallation.get();
58 | cfg.setMetadataItemSet(Collections.singleton(new MetaDataConfigItem(EventType.BUILD_EVENT.toString(), "index", "value000")));
59 | cfg.save();
60 | File xmlFile = new File(Jenkins.get().getRootDir(), cfg.getId() + ".xml");
61 | String xml = FileUtils.readFileToString(xmlFile, StandardCharsets.UTF_8);
62 | assertThat(xml, containsString("value000"));
63 | xml = xml.replace("value000", "value999");
64 | FileUtils.writeStringToFile(xmlFile, xml, StandardCharsets.UTF_8);
65 | cfg.load();
66 | Set metadataItemSet = cfg.getMetadataItemSet();
67 | assertThat(metadataItemSet, hasSize(1));
68 | assertThat(metadataItemSet.iterator().next().getValue(), is("value999"));
69 | }
70 |
71 |
72 | @Test
73 | public void testTokenConvert() {
74 | String token = "hello-token";
75 | String host = "foo-host";
76 | SplunkJenkinsInstallation cfg = SplunkJenkinsInstallation.get();
77 | cfg.setToken(Secret.fromString(token));
78 | cfg.setHost(host);
79 | Map config = cfg.toMap();
80 | // try to convert back, will set to cachedConfig
81 | RemoteUtils.initSplunkConfigOnAgent(config);
82 | cfg.setHost(null);
83 | cfg.setToken(null);
84 | // try to get the cached config which will be used for remoting
85 | cfg = SplunkJenkinsInstallation.get();
86 | assertThat(cfg.getHost(), is(host));
87 | assertThat(cfg.getTokenValue(), is(token));
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/model/AbstractTestResultAdapter.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.model;
2 |
3 | import hudson.ExtensionList;
4 | import hudson.ExtensionPoint;
5 | import hudson.model.Run;
6 | import hudson.tasks.test.AbstractTestResultAction;
7 | import hudson.tasks.test.TestResult;
8 | import org.jvnet.tiger_types.Types;
9 |
10 | import edu.umd.cs.findbugs.annotations.NonNull;
11 | import java.lang.reflect.ParameterizedType;
12 | import java.lang.reflect.Type;
13 | import java.util.ArrayList;
14 | import java.util.Collections;
15 | import java.util.List;
16 | import java.util.logging.Level;
17 |
18 | import static com.splunk.splunkjenkins.Constants.MAX_JUNIT_STDIO_SIZE;
19 |
20 | public abstract class AbstractTestResultAdapter implements ExtensionPoint {
21 | private static final java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(AbstractTestResultAdapter.class.getName());
22 |
23 | public final Class targetType;
24 |
25 | public AbstractTestResultAdapter() {
26 | Type type = Types.getBaseClass(getClass(), AbstractTestResultAdapter.class);
27 | if (type instanceof ParameterizedType)
28 | targetType = Types.erasure(Types.getTypeArgument(type, 0));
29 | else
30 | throw new IllegalStateException(getClass() + " uses the raw type for extending AbstractTestResultAdapter");
31 |
32 | }
33 |
34 | public A getAction(Run run) {
35 | return run.getAction(targetType);
36 | }
37 |
38 | public boolean isApplicable(Run build) {
39 | return getAction(build) != null;
40 | }
41 |
42 | /**
43 | * @param build jenkins build
44 | * @return all the test result added in the build
45 | */
46 | @NonNull
47 | public static List getTestResult(Run build) {
48 | return getTestResult(build, Collections.emptyList());
49 | }
50 |
51 | /**
52 | * @param build jenkins build
53 | * @param ignoredActions a list of test action class name
54 | * @return the test result filtered by the test action name
55 | */
56 | @NonNull
57 | public static List getTestResult(Run build, @NonNull List ignoredActions) {
58 | List adapters = ExtensionList.lookup(AbstractTestResultAdapter.class);
59 | List testResults = new ArrayList<>();
60 | for (AbstractTestResultAdapter adapter : adapters) {
61 | if (adapter.isApplicable(build)) {
62 | AbstractTestResultAction action = adapter.getAction(build);
63 | if (ignoredActions.contains(action.getClass().getName())) {
64 | // the test action is ignored
65 | continue;
66 | }
67 | testResults.addAll(adapter.getTestResult(action));
68 | }
69 | }
70 | return testResults;
71 | }
72 |
73 | public abstract List getTestResult(A resultAction);
74 |
75 | public static String trimToLimit(String message, String caseName, String url) {
76 | String truncatedMessage = "...truncated";
77 | if (MAX_JUNIT_STDIO_SIZE < truncatedMessage.length() || message == null || message.length() <= MAX_JUNIT_STDIO_SIZE) {
78 | return message;
79 | }
80 | // setUniqueName was called before setStdout/setStderr in JunitResultAdapter/TestNGResultAdapter
81 | LOG.log(Level.WARNING, "build_url={0} testcase={1} message=\"stdout or stderr too large\" length={2,number,#}" +
82 | " truncated_size={3,number,#}\n" +
83 | "please adjust jenkins startup option -Dsplunkins.junitStdioLimit=x if you want to avoid this",
84 | new Object[]{url, caseName, message.length(), MAX_JUNIT_STDIO_SIZE});
85 | return message.substring(0, MAX_JUNIT_STDIO_SIZE - truncatedMessage.length()) + truncatedMessage;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/splunk-devops-extend/src/test/java/com/splunk/splunkjenkins/StageStepNodesTest.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins;
2 |
3 | import hudson.model.labels.LabelAtom;
4 | import hudson.slaves.DumbSlave;
5 | import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
6 | import org.jenkinsci.plugins.workflow.job.WorkflowJob;
7 | import org.jenkinsci.plugins.workflow.job.WorkflowRun;
8 | import org.junit.Before;
9 | import org.junit.ClassRule;
10 | import org.junit.Rule;
11 | import org.junit.Test;
12 | import org.jvnet.hudson.test.BuildWatcher;
13 | import org.jvnet.hudson.test.JenkinsRule;
14 |
15 | import static com.splunk.splunkjenkins.SplunkConfigUtil.checkTokenAvailable;
16 | import static com.splunk.splunkjenkins.SplunkConfigUtil.verifySplunkSearchResult;
17 | import static org.junit.Assert.assertFalse;
18 | import static org.junit.Assert.assertTrue;
19 |
20 | public class StageStepNodesTest {
21 | @ClassRule
22 | public static BuildWatcher buildWatcher = new BuildWatcher();
23 | @Rule
24 | public JenkinsRule r = new JenkinsRule();
25 | private String jobScript = "stage(\"unit-test\"){\n" +
26 | " node(\"ci-1\"){\n" +
27 | " echo \"hello\"\n" +
28 | " echo \"hello world2\"\n" +
29 | " }\n" +
30 | " node(\"ci-2\"){\n" +
31 | " echo \"hello\"\n" +
32 | " }\n" +
33 | "}";
34 | private String nestedStage = "node{\n" +
35 | " stage('build') {\n" +
36 | " stage('linux'){\n" +
37 | " echo \"hello\"\n" +
38 | " }\n" +
39 | " stage('windows'){\n" +
40 | " echo \"hello\"\n" +
41 | " stage('build c'){\n" +
42 | " echo \"hello\"\n" +
43 | " build job:'dummy-job', wait:false \n" +
44 | " }\n" +
45 | " }\n" +
46 | " }\n" +
47 | "}";
48 |
49 | @Before
50 | public void setUp() throws Exception {
51 | org.junit.Assume.assumeTrue(checkTokenAvailable());
52 | }
53 |
54 | @Test
55 | public void testStageNodes() throws Exception {
56 | long startTime = System.currentTimeMillis();
57 | DumbSlave node = r.createOnlineSlave(new LabelAtom("ci-1"));
58 | DumbSlave node1 = r.createOnlineSlave(new LabelAtom("ci-2"));
59 | WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "stage-node-job");
60 | p.setDefinition(new CpsFlowDefinition(jobScript, true));
61 | WorkflowRun b1 = r.assertBuildStatusSuccess(p.scheduleBuild2(0));
62 | assertFalse(b1.isBuilding());
63 | r.assertLogContains("hello", b1);
64 | assertTrue(b1.getDuration() > 0);
65 | //check exec_node
66 | verifySplunkSearchResult("\"stages{}.children{}.exec_node\"=\"" + node.getNodeName() + "\"", startTime, 1);
67 | verifySplunkSearchResult("\"stages{}.children{}.exec_node\"=\"" + node1.getNodeName() + "\"", startTime, 1);
68 | }
69 |
70 | @Test
71 | public void testFlattenStageResult() throws Exception {
72 | long startTime = System.currentTimeMillis();
73 | WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "nested-stage-job");
74 | p.setDefinition(new CpsFlowDefinition(nestedStage, true));
75 | WorkflowJob dummy = r.jenkins.createProject(WorkflowJob.class, "dummy-job");
76 | dummy.setDefinition(new CpsFlowDefinition("node{}", true));
77 | WorkflowRun b1 = r.assertBuildStatusSuccess(p.scheduleBuild2(0));
78 | assertFalse(b1.isBuilding());
79 | r.assertLogContains("hello", b1);
80 | assertTrue(b1.getDuration() > 0);
81 | //check stage count
82 | verifySplunkSearchResult("type=completed build_url=" + b1.getUrl() + "|spath output=stages path=\"stages{}\"" +
83 | "| mvexpand stages" +
84 | "| table stages", startTime, 4);
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/model/TestCaseResult.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.model;
2 |
3 | import hudson.tasks.test.TestObject;
4 | import hudson.tasks.test.TestResult;
5 |
6 | public class TestCaseResult extends TestResult {
7 | private float duration;
8 | private String className;
9 | private String testName;
10 | private String groupName;
11 | private boolean skipped;
12 | private String skippedMessage;
13 | private String errorStackTrace;
14 | private String errorDetails;
15 | private String stdout, stderr;
16 | private int failedSince;
17 | private String uniqueName;
18 | private TestStatus status;
19 |
20 | @Override
21 | public TestObject getParent() {
22 | return null;
23 | }
24 |
25 | @Override
26 | public TestResult findCorrespondingResult(String id) {
27 | return null;
28 | }
29 |
30 | @Override
31 | public String getDisplayName() {
32 | return null;
33 | }
34 |
35 | @Override
36 | public float getDuration() {
37 | return duration;
38 | }
39 |
40 | public void setDuration(float duration) {
41 | this.duration = duration;
42 | }
43 |
44 | public String getClassName() {
45 | return className;
46 | }
47 |
48 | public void setClassName(String className) {
49 | this.className = className;
50 | }
51 |
52 | public String getTestName() {
53 | return testName;
54 | }
55 |
56 | public void setTestName(String testName) {
57 | this.testName = testName;
58 | }
59 |
60 | public boolean isSkipped() {
61 | return skipped;
62 | }
63 |
64 | public void setSkipped(boolean skipped) {
65 | this.skipped = skipped;
66 | }
67 |
68 | public String getSkippedMessage() {
69 | return skippedMessage;
70 | }
71 |
72 | public void setSkippedMessage(String skippedMessage) {
73 | this.skippedMessage = skippedMessage;
74 | }
75 |
76 | @Override
77 | public String getErrorStackTrace() {
78 | return errorStackTrace;
79 | }
80 |
81 | public void setErrorStackTrace(String errorStackTrace) {
82 | this.errorStackTrace = errorStackTrace;
83 | }
84 |
85 | @Override
86 | public String getErrorDetails() {
87 | return errorDetails;
88 | }
89 |
90 | public void setErrorDetails(String errorDetails) {
91 | this.errorDetails = errorDetails;
92 | }
93 |
94 | @Override
95 | public String getStdout() {
96 | return stdout;
97 | }
98 |
99 | public void setStdout(String stdout) {
100 | this.stdout = stdout;
101 | }
102 |
103 | @Override
104 | public String getStderr() {
105 | return stderr;
106 | }
107 |
108 | public void setStderr(String stderr) {
109 | this.stderr = stderr;
110 | }
111 |
112 | @Override
113 | public int getFailedSince() {
114 | return failedSince;
115 | }
116 |
117 | public void setFailedSince(int failedSince) {
118 | this.failedSince = failedSince;
119 | }
120 |
121 | public String getUniqueName() {
122 | return uniqueName;
123 | }
124 |
125 | public void setUniqueName(String uniqueName) {
126 | this.uniqueName = uniqueName;
127 | }
128 |
129 | public TestStatus getStatus() {
130 | return status;
131 | }
132 |
133 | public void setStatus(TestStatus status) {
134 | this.status = status;
135 | }
136 |
137 | @Override
138 | public int getPassCount() {
139 | return TestStatus.PASSED == status ? 1 : 0;
140 | }
141 |
142 | @Override
143 | public int getFailCount() {
144 | return TestStatus.FAILURE == status ? 1 : 0;
145 | }
146 |
147 | @Override
148 | public int getSkipCount() {
149 | return TestStatus.SKIPPED == status ? 1 : 0;
150 | }
151 |
152 | public String getGroupName() {
153 | return groupName;
154 | }
155 |
156 | public void setGroupName(String groupName) {
157 | this.groupName = groupName;
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/splunk-devops-extend/src/main/java/com/splunk/splunkjenkins/SplunkConsoleLogStep.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins;
2 |
3 | import com.google.common.collect.ImmutableSet;
4 | import com.splunk.splunkjenkins.console.ConsoleRecordCacheUtils;
5 | import com.splunk.splunkjenkins.console.SplunkConsoleTaskListenerDecorator;
6 | import hudson.Extension;
7 | import hudson.model.Run;
8 | import org.jenkinsci.plugins.workflow.job.WorkflowRun;
9 | import org.jenkinsci.plugins.workflow.log.TaskListenerDecorator;
10 | import org.jenkinsci.plugins.workflow.steps.BodyExecutionCallback;
11 | import org.jenkinsci.plugins.workflow.steps.BodyInvoker;
12 | import org.jenkinsci.plugins.workflow.steps.Step;
13 | import org.jenkinsci.plugins.workflow.steps.StepContext;
14 | import org.jenkinsci.plugins.workflow.steps.StepDescriptor;
15 | import org.jenkinsci.plugins.workflow.steps.StepExecution;
16 | import org.kohsuke.stapler.DataBoundConstructor;
17 |
18 | import edu.umd.cs.findbugs.annotations.NonNull;
19 | import java.util.Set;
20 | import java.util.logging.Level;
21 | import java.util.logging.Logger;
22 |
23 | public class SplunkConsoleLogStep extends Step {
24 | private static final Logger LOG = Logger.getLogger(SplunkConsoleLogStep.class.getName());
25 |
26 | @DataBoundConstructor
27 | public SplunkConsoleLogStep() {
28 | }
29 |
30 | @Override
31 | public StepExecution start(StepContext context) throws Exception {
32 | return new ConsoleLogExecutionImpl(context);
33 | }
34 |
35 | @Extension(optional = true)
36 | public static class DescriptorImpl extends StepDescriptor {
37 | @Override
38 | public Set extends Class>> getRequiredContext() {
39 | return ImmutableSet.of(Run.class);
40 | }
41 |
42 | /**
43 | * {@inheritDoc}
44 | */
45 | @Override
46 | public String getFunctionName() {
47 | return "sendSplunkConsoleLog";
48 | }
49 |
50 | /**
51 | * {@inheritDoc}
52 | */
53 | @NonNull
54 | @Override
55 | public String getDisplayName() {
56 | return "Send console log Splunk";
57 | }
58 |
59 | /**
60 | * {@inheritDoc}
61 | */
62 | @Override
63 | public boolean takesImplicitBlockArgument() {
64 | return true;
65 | }
66 | }
67 |
68 |
69 | public static class ConsoleLogExecutionImpl extends StepExecution {
70 | public ConsoleLogExecutionImpl(StepContext context) {
71 | super(context);
72 | }
73 |
74 | /**
75 | * {@inheritDoc}
76 | */
77 | @Override
78 | public boolean start() throws Exception {
79 | //refer to WithContextStep implementation
80 | StepContext context = getContext();
81 | Run run = context.get(Run.class);
82 | BodyInvoker invoker = context.newBodyInvoker().withCallback(new BodyExecutionCallbackConsole());
83 | if (!SplunkJenkinsInstallation.get().isPipelineFilterEnabled()) {
84 | invoker.withContext(TaskListenerDecorator.merge(context.get(TaskListenerDecorator.class), new SplunkConsoleTaskListenerDecorator((WorkflowRun) run)));
85 | } else {
86 | String jobName = run.getParent().getFullName();
87 | LOG.log(Level.INFO, "ignored sendSplunkConsoleLog since global filter is enabled, job-name=" + jobName);
88 | }
89 | invoker.start();
90 | return false;
91 | }
92 |
93 | /**
94 | * {@inheritDoc}
95 | */
96 | @Override
97 | public void stop(@NonNull Throwable cause) throws Exception {
98 | getContext().onFailure(cause);
99 | }
100 | }
101 |
102 | public static class BodyExecutionCallbackConsole extends BodyExecutionCallback.TailCall {
103 | private static final long serialVersionUID = 1L;
104 |
105 | @Override
106 | protected void finished(StepContext stepContext) throws Exception {
107 | ConsoleRecordCacheUtils.flushLog();
108 | }
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/splunk-devops-extend/src/main/java/com/splunk/splunkjenkins/SplunkLogFileStep.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins;
2 |
3 | import com.google.common.collect.ImmutableSet;
4 | import hudson.EnvVars;
5 | import hudson.Extension;
6 | import hudson.FilePath;
7 | import hudson.model.Run;
8 | import hudson.model.TaskListener;
9 | import org.jenkinsci.plugins.workflow.steps.Step;
10 | import org.jenkinsci.plugins.workflow.steps.StepContext;
11 | import org.jenkinsci.plugins.workflow.steps.StepDescriptor;
12 | import org.jenkinsci.plugins.workflow.steps.StepExecution;
13 | import org.jenkinsci.plugins.workflow.steps.SynchronousNonBlockingStepExecution;
14 | import org.kohsuke.stapler.DataBoundConstructor;
15 | import org.kohsuke.stapler.DataBoundSetter;
16 |
17 | import edu.umd.cs.findbugs.annotations.NonNull;
18 | import java.util.Set;
19 |
20 | import static com.splunk.splunkjenkins.utils.LogEventHelper.parseFileSize;
21 | import static com.splunk.splunkjenkins.utils.LogEventHelper.sendFiles;
22 |
23 | /**
24 | * Send logs to splunk
25 | */
26 | public class SplunkLogFileStep extends Step {
27 | //required fields
28 | String includes;
29 |
30 | @DataBoundSetter
31 | String sizeLimit;
32 | @DataBoundSetter
33 | String excludes;
34 | @DataBoundSetter
35 | boolean publishFromSlave;
36 |
37 | @DataBoundConstructor
38 | public SplunkLogFileStep(@NonNull String includes) {
39 | this.includes = includes;
40 | }
41 |
42 | @Override
43 | public StepExecution start(StepContext context) throws Exception {
44 | return new SplunkLogFileStepExecution(context, this);
45 | }
46 |
47 | public String getIncludes() {
48 | return includes;
49 | }
50 |
51 | public void setIncludes(String includes) {
52 | this.includes = includes;
53 | }
54 |
55 | public String getExcludes() {
56 | return excludes;
57 | }
58 |
59 | public void setExcludes(String excludes) {
60 | this.excludes = excludes;
61 | }
62 |
63 | public boolean isPublishFromSlave() {
64 | return publishFromSlave;
65 | }
66 |
67 | public void setPublishFromSlave(boolean publishFromSlave) {
68 | this.publishFromSlave = publishFromSlave;
69 | }
70 |
71 | public String getSizeLimit() {
72 | return sizeLimit;
73 | }
74 |
75 | public void setSizeLimit(String sizeLimit) {
76 | this.sizeLimit = sizeLimit;
77 | }
78 |
79 | @Extension
80 | public static class DescriptorImpl extends StepDescriptor {
81 |
82 | @Override
83 | public Set extends Class>> getRequiredContext() {
84 | return ImmutableSet.of(Run.class, TaskListener.class, FilePath.class, EnvVars.class);
85 | }
86 |
87 | @Override
88 | public String getFunctionName() {
89 | return "sendSplunkFile";
90 | }
91 |
92 | @NonNull
93 | @Override
94 | public String getDisplayName() {
95 | return "Send files to Splunk";
96 | }
97 | }
98 |
99 | public static class SplunkLogFileStepExecution extends SynchronousNonBlockingStepExecution {
100 | protected SplunkLogFileStepExecution(StepContext context, SplunkLogFileStep step) throws Exception {
101 | super(context);
102 | this.step = step;
103 | }
104 |
105 | private static final long serialVersionUID = 1152009261375345133L;
106 | private transient SplunkLogFileStep step;
107 |
108 | @Override
109 | protected Void run() throws Exception {
110 | if (!SplunkJenkinsInstallation.get().isEnabled()) {
111 | return null;
112 | }
113 | TaskListener listener = getContext().get(TaskListener.class);
114 | FilePath workspace = getContext().get(FilePath.class);
115 | Run build = getContext().get(Run.class);
116 | EnvVars envVars = getContext().get(EnvVars.class);
117 | sendFiles(build, workspace, envVars, listener,
118 | step.includes, step.excludes, step.publishFromSlave, parseFileSize(step.sizeLimit));
119 | return null;
120 | }
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/model/CloverCoverageMetrics.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.model;
2 |
3 | import hudson.Extension;
4 | import hudson.plugins.clover.CloverBuildAction;
5 | import hudson.plugins.clover.Ratio;
6 | import hudson.plugins.clover.results.*;
7 |
8 | import java.util.ArrayList;
9 | import java.util.HashMap;
10 | import java.util.List;
11 | import java.util.Map;
12 |
13 | import static com.splunk.splunkjenkins.Constants.COVERAGE_OVERALL_NAME;
14 |
15 | /**
16 | * CoverageMetric for clover
17 | */
18 | @Extension(optional = true)
19 | public class CloverCoverageMetrics extends CoverageMetricsAdapter {
20 | /**
21 | * @return coverage summary
22 | * {@inheritDoc}
23 | */
24 | @Override
25 | public Map getMetrics(CloverBuildAction coverageAction) {
26 | ProjectCoverage projectCoverage = coverageAction.getResult();
27 | Map result = extract(projectCoverage);
28 | return result;
29 | }
30 |
31 | @Override
32 | public List getReport(CloverBuildAction coverageAction) {
33 | ProjectCoverage projectCoverage = coverageAction.getResult();
34 | List result = new ArrayList<>();
35 | CoverageDetail summary = new CoverageDetail(COVERAGE_OVERALL_NAME, CoverageLevel.PROJECT);
36 | result.add(summary);
37 | appendDetail(summary, coverageAction);
38 | for (PackageCoverage pcover : projectCoverage.getChildren()) {
39 | CoverageDetail packageDetail = new CoverageDetail(pcover.getName(), CoverageLevel.PACKAGE);
40 | result.add(packageDetail);
41 | appendDetail(packageDetail, pcover);
42 | for (FileCoverage fcover : pcover.getChildren()) {
43 | CoverageDetail fileDetail = new CoverageDetail(pcover.getName(), CoverageLevel.FILE);
44 | result.add(fileDetail);
45 | appendDetail(fileDetail, fcover);
46 | for (ClassCoverage clazzCover : fcover.getChildren()) {
47 | CoverageDetail clazzDetail = new CoverageDetail(clazzCover.getName(), CoverageLevel.CLASS);
48 | result.add(clazzDetail);
49 | appendDetail(clazzDetail, clazzCover);
50 | }
51 | }
52 | }
53 | return result;
54 | }
55 |
56 | private Map extract(AbstractCloverMetrics coverageObject) {
57 | Map result = new HashMap<>();
58 | putMetricIfExists(result, Metric.METHOD, coverageObject.getMethodCoverage());
59 | putMetricIfExists(result, Metric.STATEMENT, coverageObject.getStatementCoverage());
60 | putMetricIfExists(result, Metric.CONDITIONAL, coverageObject.getConditionalCoverage());
61 | putMetricIfExists(result, Metric.ELEMENT, coverageObject.getElementCoverage());
62 | return result;
63 | }
64 |
65 | private void putMetricIfExists(Map result, Metric metric, Ratio ratio) {
66 | if (ratio.denominator > 0) {
67 | result.put(metric, ratio.getPercentage());
68 | }
69 | }
70 |
71 | /**
72 | * get detail report about percentage, covered, and total number
73 | *
74 | * @param detail
75 | * @param coverageObject
76 | */
77 | private void appendDetail(CoverageDetail detail, AbstractCloverMetrics coverageObject) {
78 | appendDetail(detail, Metric.METHOD, coverageObject.getMethodCoverage());
79 | appendDetail(detail, Metric.STATEMENT, coverageObject.getStatementCoverage());
80 | appendDetail(detail, Metric.CONDITIONAL, coverageObject.getConditionalCoverage());
81 | appendDetail(detail, Metric.ELEMENT, coverageObject.getElementCoverage());
82 | }
83 |
84 | private void appendDetail(CoverageDetail detail, Metric metricName, Ratio ratio) {
85 | if (ratio.denominator == 0) {
86 | return;
87 | }
88 | detail.add(metricName + PERCENTAGE_SUFFIX, ratio.getPercentage());
89 | detail.add(metricName + TOTAL_SUFFIX, (int) ratio.denominator);
90 | detail.add(metricName + COVERED_SUFFIX, (int) ratio.numerator);
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/SplunkArtifactNotifier.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins;
2 |
3 | import hudson.Extension;
4 | import hudson.FilePath;
5 | import hudson.Launcher;
6 | import hudson.model.*;
7 | import hudson.tasks.BuildStepDescriptor;
8 | import hudson.tasks.BuildStepMonitor;
9 | import hudson.tasks.Notifier;
10 | import hudson.tasks.Publisher;
11 | import jenkins.tasks.SimpleBuildStep;
12 | import org.kohsuke.stapler.DataBoundConstructor;
13 |
14 | import edu.umd.cs.findbugs.annotations.NonNull;
15 | import java.io.IOException;
16 | import java.util.HashMap;
17 | import java.util.Map;
18 | import java.util.logging.Level;
19 | import java.util.logging.Logger;
20 |
21 | import static com.splunk.splunkjenkins.utils.LogEventHelper.parseFileSize;
22 | import static com.splunk.splunkjenkins.utils.LogEventHelper.sendFiles;
23 |
24 | @SuppressWarnings("unused")
25 | public class SplunkArtifactNotifier extends Notifier implements SimpleBuildStep {
26 | /**
27 | * {@link org.apache.tools.ant.types.FileSet} "includes" string, like "foo/bar/*.xml"
28 | */
29 | private final String includeFiles;
30 | private final String excludeFiles;
31 | private final boolean publishFromSlave;
32 | private final boolean skipGlobalSplunkArchive;
33 | private final String sizeLimit;
34 |
35 | @DataBoundConstructor
36 | public SplunkArtifactNotifier(String includeFiles, String excludeFiles, boolean publishFromSlave,
37 | boolean skipGlobalSplunkArchive, String sizeLimit) {
38 | this.includeFiles = includeFiles;
39 | this.excludeFiles = excludeFiles;
40 | this.publishFromSlave = publishFromSlave;
41 | this.skipGlobalSplunkArchive = skipGlobalSplunkArchive;
42 | this.sizeLimit=sizeLimit;
43 | }
44 |
45 | @Override
46 | public BuildStepMonitor getRequiredMonitorService() {
47 | return BuildStepMonitor.NONE;
48 | }
49 |
50 | @Override
51 | public void perform(@NonNull Run, ?> build, @NonNull FilePath workspace,
52 | @NonNull Launcher launcher, @NonNull TaskListener listener) throws InterruptedException, IOException {
53 | Map envVars = new HashMap<>();
54 | try {
55 | envVars = build.getEnvironment(listener);
56 | } catch (Exception ex) {
57 | listener.getLogger().println("failed to get env");
58 | }
59 | long maxFileSize=parseFileSize(sizeLimit);
60 | listener.getLogger().println("sending files at job level, includes:" + includeFiles + " excludes:" + excludeFiles);
61 | int eventCount = sendFiles(build, workspace, envVars, listener, includeFiles, excludeFiles, publishFromSlave, maxFileSize);
62 | Logger.getLogger(this.getClass().getName()).log(Level.FINE,"sent "+eventCount+" events with file size limit "+maxFileSize);
63 | }
64 |
65 | @Extension
66 | public static class DescriptorImpl extends BuildStepDescriptor {
67 | @Override
68 | public boolean isApplicable(@SuppressWarnings("rawtypes") Class extends AbstractProject> jobType) {
69 | return true;
70 | }
71 |
72 | public String getDisplayName() {
73 | return Messages.SplunArtifactArchive();
74 | }
75 | }
76 |
77 | @Override
78 | public String toString() {
79 | return "SplunkArtifactNotifier{" +
80 | "includeFiles='" + includeFiles + '\'' +
81 | ", excludeFiles='" + excludeFiles + '\'' +
82 | ", publishFromSlave=" + publishFromSlave +
83 | ", skipGlobalSplunkArchive=" + skipGlobalSplunkArchive +
84 | ", sizeLimit='" + sizeLimit + '\'' +
85 | '}';
86 | }
87 |
88 | public String getIncludeFiles() {
89 | return includeFiles;
90 | }
91 |
92 | public String getExcludeFiles() {
93 | return excludeFiles;
94 | }
95 |
96 | public boolean isPublishFromSlave() {
97 | return publishFromSlave;
98 | }
99 |
100 | public boolean isSkipGlobalSplunkArchive() {
101 | return skipGlobalSplunkArchive;
102 | }
103 |
104 | public String getSizeLimit() {
105 | return sizeLimit;
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/splunk-devops/src/main/resources/com/splunk/splunkjenkins/SplunkJenkinsInstallation/config.jelly:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
13 |
14 |
15 |
16 |
17 |
18 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
58 |
60 |
61 |
62 |
63 |
66 |
68 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/splunk-devops/src/test/resources/com/splunk/splunkjenkins/CoverageMetricTest/jobs/Cobertura/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | false
6 |
7 |
8 | false
9 |
10 |
11 | false
12 | false
13 |
14 |
15 | 0
16 | 0
17 |
18 | false
19 | project
20 |
21 |
22 |
23 | true
24 | false
25 | false
26 | false
27 |
28 | false
29 |
30 |
31 |
32 | coverage.xml
33 | false
34 | false
35 | false
36 | false
37 | false
38 | false
39 | 0
40 | true
41 |
42 |
43 |
44 | METHOD
45 | 8000000
46 |
47 |
48 | LINE
49 | 8000000
50 |
51 |
52 | CONDITIONAL
53 | 7000000
54 |
55 |
56 |
57 |
58 |
59 |
60 | METHOD
61 | 0
62 |
63 |
64 | LINE
65 | 0
66 |
67 |
68 | CONDITIONAL
69 | 0
70 |
71 |
72 |
73 |
74 |
75 |
76 | METHOD
77 | 0
78 |
79 |
80 | LINE
81 | 0
82 |
83 |
84 | CONDITIONAL
85 | 0
86 |
87 |
88 |
89 | ASCII
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/splunk-devops-extend/src/test/java/com/splunk/splunkjenkins/SplunkConsoleLogStepTest.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins;
2 |
3 | import com.splunk.splunkjenkins.console.PipelineConsoleDecoder;
4 | import org.apache.commons.lang3.StringUtils;
5 | import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
6 | import org.jenkinsci.plugins.workflow.flow.FlowDurabilityHint;
7 | import org.jenkinsci.plugins.workflow.job.WorkflowJob;
8 | import org.jenkinsci.plugins.workflow.job.WorkflowRun;
9 | import org.jenkinsci.plugins.workflow.job.properties.DurabilityHintJobProperty;
10 | import org.junit.Before;
11 | import org.junit.ClassRule;
12 | import org.junit.Rule;
13 | import org.junit.Test;
14 | import org.jvnet.hudson.test.BuildWatcher;
15 | import org.jvnet.hudson.test.JenkinsRule;
16 |
17 | import java.io.ByteArrayOutputStream;
18 | import java.util.UUID;
19 |
20 | import static com.splunk.splunkjenkins.SplunkConfigUtil.checkTokenAvailable;
21 | import static com.splunk.splunkjenkins.SplunkConfigUtil.verifySplunkSearchResult;
22 | import static com.splunk.splunkjenkins.console.LabelConsoleLineStream.ANSI_COLOR_ESCAPE;
23 | import static org.junit.Assert.assertEquals;
24 | import static org.junit.Assert.assertFalse;
25 | import static org.junit.Assert.assertTrue;
26 |
27 | public class SplunkConsoleLogStepTest {
28 | @ClassRule
29 | public static BuildWatcher buildWatcher = new BuildWatcher();
30 | @Rule
31 | public JenkinsRule r = new JenkinsRule();
32 | String id = UUID.randomUUID().toString();
33 |
34 | private String jobScript = "sendSplunkConsoleLog {" +
35 | "node{\n" +
36 | " sh \"echo testjob\";\n" +
37 | " sh \"echo " + id + "\";\n" +
38 | " }" +
39 | "}";
40 |
41 | @Before
42 | public void setUp() throws Exception {
43 | org.junit.Assume.assumeTrue(checkTokenAvailable());
44 | }
45 |
46 | @Test
47 | public void testSendConsoleLog() throws Exception {
48 | long startTime = System.currentTimeMillis();
49 | WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "p");
50 | p.setDefinition(new CpsFlowDefinition(jobScript, true));
51 | WorkflowRun b1 = r.assertBuildStatusSuccess(p.scheduleBuild2(0));
52 | assertFalse(b1.isBuilding());
53 | r.assertLogContains("testjob", b1);
54 | assertTrue(b1.getDuration() > 0);
55 | //check log
56 | verifySplunkSearchResult("source=" + b1.getUrl() + "console " + id, startTime, 1);
57 | }
58 |
59 | @Test
60 | public void testDecodeLine() throws Exception {
61 | WorkflowJob p = r.createProject(WorkflowJob.class, "p");
62 | p.addProperty(new DurabilityHintJobProperty(FlowDurabilityHint.SURVIVABLE_NONATOMIC));
63 | p.setDefinition(new CpsFlowDefinition("parallel first: {echo 'hello'}, second: {echo 'in-second'};", true));
64 | WorkflowRun b = r.buildAndAssertSuccess(p);
65 | ByteArrayOutputStream out = new ByteArrayOutputStream();
66 | b.getLogText().writeRawLogTo(0, out);
67 | PipelineConsoleDecoder decoder = new PipelineConsoleDecoder(b);
68 | byte[] logs = out.toByteArray();
69 | assertTrue("end with \n", logs[logs.length - 1] == '\n');
70 | StringBuffer lines = new StringBuffer();
71 | // decode line by line
72 | ByteArrayOutputStream branch = new ByteArrayOutputStream();
73 | int lineCount = 0;
74 | for (int i = 0; i < logs.length; i++) {
75 | branch.write(logs[i]);
76 | if (logs[i] == '\n') {
77 | lines.append(decoder.decodeLine(branch.toByteArray(), branch.size()));
78 | branch.reset();
79 | lineCount++;
80 | }
81 | }
82 | String text = lines.toString();
83 | assertTrue(text.contains("parallel_label=\"first\" [Pipeline] echo"));
84 | assertTrue(text.contains("parallel_label=\"first\" hello"));
85 | assertTrue(text.contains("parallel_label=\"second\" [Pipeline] echo"));
86 | assertTrue(text.contains("parallel_label=\"second\" in-second"));
87 | assertEquals(lineCount, StringUtils.countMatches(text, "\n"));
88 | assertEquals(2, StringUtils.countMatches(text, "label=\"second\" "));
89 | }
90 |
91 | @Test
92 | public void testAnsilColor() {
93 | String lineWithAnsi = "\u001B[0;31mline in red\nend\u001B[0m";
94 | String line = ANSI_COLOR_ESCAPE.matcher(lineWithAnsi).replaceAll("");
95 | assertEquals("line in red\nend", line);
96 | }
97 | }
--------------------------------------------------------------------------------
/splunk-devops/src/main/java/com/splunk/splunkjenkins/listeners/LoggingConfigListener.java:
--------------------------------------------------------------------------------
1 | package com.splunk.splunkjenkins.listeners;
2 |
3 | import com.splunk.splunkjenkins.SplunkJenkinsInstallation;
4 | import com.splunk.splunkjenkins.utils.SplunkLogService;
5 | import hudson.Extension;
6 | import hudson.XmlFile;
7 | import hudson.model.Item;
8 | import hudson.model.Saveable;
9 | import hudson.model.User;
10 | import hudson.model.listeners.SaveableListener;
11 | import jenkins.model.Jenkins;
12 | import org.apache.commons.codec.digest.DigestUtils;
13 |
14 | import java.io.IOException;
15 | import java.util.WeakHashMap;
16 | import java.util.logging.Level;
17 | import java.util.logging.Logger;
18 | import java.util.regex.Pattern;
19 | import java.util.regex.PatternSyntaxException;
20 |
21 | import static com.splunk.splunkjenkins.Constants.JENKINS_CONFIG_PREFIX;
22 | import static com.splunk.splunkjenkins.model.EventType.JENKINS_CONFIG;
23 | import static com.splunk.splunkjenkins.utils.LogEventHelper.getRelativeJenkinsHomePath;
24 | import static com.splunk.splunkjenkins.utils.LogEventHelper.getUserName;
25 | import static com.splunk.splunkjenkins.utils.LogEventHelper.logUserAction;
26 |
27 | /**
28 | * record jenkins config and job changes
29 | * send config content to splunk
30 | */
31 |
32 | @edu.umd.cs.findbugs.annotations.SuppressFBWarnings("VA_FORMAT_STRING_USES_NEWLINE")
33 | @Extension
34 | public class LoggingConfigListener extends SaveableListener {
35 | private static final String XML_COMMENT = "\n";
36 | private static final Logger LOGGER = Logger.getLogger(LoggingConfigListener.class.getName());
37 | //queue.xml or build/*/config.xml
38 | private static final String IGNORE_CONFIG_CHANGE_PATTERN = "(?:queue|nodeMonitors|UpdateCenter|global-build-stats).xml|" +
39 | "/(?:fingerprint|builds|config-history)/.*?xml";
40 | public static final Pattern IGNORED;
41 |
42 | static {
43 | String ignorePatternStr = System.getProperty("splunkins.ignoreConfigChangePattern", IGNORE_CONFIG_CHANGE_PATTERN);
44 | Pattern ignorePattern;
45 | try {
46 | ignorePattern = Pattern.compile(ignorePatternStr, Pattern.CASE_INSENSITIVE);
47 | } catch (PatternSyntaxException ex) {
48 | ignorePattern = Pattern.compile(IGNORE_CONFIG_CHANGE_PATTERN, Pattern.CASE_INSENSITIVE);
49 | }
50 | IGNORED = ignorePattern;
51 | }
52 |
53 | private WeakHashMap cached = new WeakHashMap(512);
54 |
55 | @Override
56 | public void onChange(Saveable saveable, XmlFile file) {
57 | if (!SplunkJenkinsInstallation.isLogHandlerRegistered()) {
58 | return;
59 | }
60 | String configPath = file.getFile().getAbsolutePath();
61 | if (saveable == null || IGNORED.matcher(configPath).find()) {
62 | LOGGER.log(Level.FINE, "{} is ignored", configPath);
63 | return;
64 | }
65 | if (saveable instanceof User) {
66 | //we use SecurityListener to capture login/logout events
67 | return;
68 | }
69 | if (SplunkJenkinsInstallation.get().isEventDisabled(JENKINS_CONFIG)) {
70 | return;
71 | }
72 | //log audit trail, excludes Item instances which were already tracked by other listener
73 | String relativePath = getRelativeJenkinsHomePath(configPath);
74 | if (!(saveable instanceof Item)) {
75 | logUserAction(getUserName(), Messages.audit_update_item(relativePath));
76 | }
77 | if ("SYSTEM".equals(Jenkins.getAuthentication().getName())) {
78 | LOGGER.log(Level.FINE, "{0} is changed by system", configPath);
79 | //ignore changes made by daemons or background jobs
80 | return;
81 | }
82 | try {
83 | String configContent = file.asString();
84 | String checkSum = DigestUtils.md5Hex(configPath + configContent);
85 | if (cached.containsKey(checkSum)) {
86 | //Save a job can trigger multiple SaveableListener, depends on jenkins versions
87 | // e.g. AbstractProject.submit may call setters which can trigger save()
88 | return;
89 | }
90 | cached.put(checkSum, 0);
91 | String sourceName = JENKINS_CONFIG_PREFIX + relativePath;
92 | String userName = getUserName();
93 | String comment = String.format(XML_COMMENT, userName);
94 | SplunkLogService.getInstance().send(comment + configContent, JENKINS_CONFIG, sourceName);
95 | } catch (IOException e) {
96 | //just ignore
97 | }
98 | }
99 | }
100 |
--------------------------------------------------------------------------------