mirror of
https://github.com/SAP/jenkins-library.git
synced 2026-06-19 22:58:55 +02:00
extend Analytics (#439)
* add extension mechanism for analytics * add sha1 hashing * correct return types * correct registerEventListener method * decrese visibility of createInstance * correct typo * catch exceptions from tests * correct test case * Update Analytics.groovy * rename to Telemetry * rename file * fix typo * add test case for generateSha1 * expose methods to tests * add clear method for tests * change return type * add test for Telemetry class * replace UtilsTests * remove unused imports * make default reporter static * add stage parameters to payload * simplify SHA1 method * remove obsolete method * remove obsolete methods * remove outdated tests
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
package com.sap.piper
|
||||
|
||||
import com.cloudbees.groovy.cps.NonCPS
|
||||
import org.jenkinsci.plugins.workflow.steps.MissingContextVariableException
|
||||
import com.sap.piper.analytics.Telemetry
|
||||
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.security.MessageDigest
|
||||
|
||||
@NonCPS
|
||||
def getMandatoryParameter(Map map, paramName, defaultValue = null) {
|
||||
@@ -72,67 +75,23 @@ def unstashAll(stashContent) {
|
||||
return unstashedContent
|
||||
}
|
||||
|
||||
def generateSha1Inline(input) {
|
||||
return "`echo -n '${input}' | sha1sum | sed 's/ -//'`"
|
||||
@NonCPS
|
||||
def generateSha1(input) {
|
||||
return MessageDigest
|
||||
.getInstance("SHA-1")
|
||||
.digest(input.getBytes(StandardCharsets.UTF_8))
|
||||
.encodeHex().toString()
|
||||
}
|
||||
|
||||
void pushToSWA(Map parameters, Map config) {
|
||||
try {
|
||||
//allow opt-out via configuration
|
||||
if (!config?.collectTelemetryData) {
|
||||
echo "[${parameters.get('step')}] Telemetry Report to SWA disabled!"
|
||||
return
|
||||
}
|
||||
parameters.actionName = parameters.get('actionName') ?: 'Piper Library OS'
|
||||
parameters.eventType = parameters.get('eventType') ?: 'library-os'
|
||||
parameters.jobUrlSha1 = generateSha1(env.JOB_URL)
|
||||
parameters.buildUrlSha1 = generateSha1(env.BUILD_URL)
|
||||
|
||||
def swaCustom = [:]
|
||||
|
||||
/* SWA custom parameters:
|
||||
custom3 = step name (passed as parameter step)
|
||||
custom4 = job url hashed (calculated)
|
||||
custom5 = build url hashed (calculated)
|
||||
custom10 = stage name
|
||||
custom11 = step related parameter 1 (passed as parameter stepParam1)
|
||||
custom12 = step related parameter 2 (passed as parameter stepParam2)
|
||||
custom13 = step related parameter 3 (passed as parameter stepParam3)
|
||||
custom14 = step related parameter 4 (passed as parameter stepParam4)
|
||||
custom15 = step related parameter 5 (passed as parameter stepParam5)
|
||||
*/
|
||||
|
||||
def swaUrl = 'https://webanalytics.cfapps.eu10.hana.ondemand.com/tracker/log'
|
||||
def action_name = 'Piper Library OS'
|
||||
def idsite = '827e8025-1e21-ae84-c3a3-3f62b70b0130'
|
||||
def url = 'https://github.com/SAP/jenkins-library'
|
||||
def event_type = parameters.get('eventType') ?: 'library-os'
|
||||
|
||||
swaCustom.custom3 = parameters.get('step')
|
||||
swaCustom.custom4 = generateSha1Inline(env.JOB_URL)
|
||||
swaCustom.custom5 = generateSha1Inline(env.BUILD_URL)
|
||||
swaCustom.custom10 = parameters.get('stageName')
|
||||
swaCustom.custom11 = parameters.get('stepParam1')
|
||||
swaCustom.custom12 = parameters.get('stepParam2')
|
||||
swaCustom.custom13 = parameters.get('stepParam3')
|
||||
swaCustom.custom14 = parameters.get('stepParam4')
|
||||
swaCustom.custom15 = parameters.get('stepParam5')
|
||||
|
||||
def options = []
|
||||
options.push("-G")
|
||||
options.push("-v \"${swaUrl}\"")
|
||||
options.push("--data-urlencode \"action_name=${action_name}\"")
|
||||
options.push("--data-urlencode \"idsite=${idsite}\"")
|
||||
options.push("--data-urlencode \"url=${url}\"")
|
||||
options.push("--data-urlencode \"event_type=${event_type}\"")
|
||||
for(def key : ['custom3', 'custom4', 'custom5', 'custom10', 'custom11', 'custom12', 'custom13', 'custom14', 'custom15']){
|
||||
if (swaCustom[key] != null) options.push("--data-urlencode \"${key}=${swaCustom[key]}\"")
|
||||
}
|
||||
options.push("--connect-timeout 5")
|
||||
options.push("--max-time 20")
|
||||
|
||||
sh(returnStatus: true, script: "#!/bin/sh +x\ncurl ${options.join(' ')} > /dev/null 2>&1 || echo '[${parameters.get('step')}] Telemetry Report to SWA failed!'")
|
||||
|
||||
} catch (MissingContextVariableException noNode) {
|
||||
echo "[${parameters.get('step')}] Telemetry Report to SWA skipped, no node available!"
|
||||
Telemetry.notify(this, config, parameters)
|
||||
} catch (ignore) {
|
||||
// some error occured in SWA reporting. This should not break anything though.
|
||||
// some error occured in telemetry reporting. This should not break anything though.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.sap.piper.analytics
|
||||
|
||||
import com.cloudbees.groovy.cps.NonCPS
|
||||
import org.jenkinsci.plugins.workflow.steps.MissingContextVariableException
|
||||
|
||||
class Telemetry implements Serializable{
|
||||
|
||||
protected static Telemetry instance
|
||||
|
||||
protected List listenerList = []
|
||||
|
||||
protected Telemetry(){}
|
||||
|
||||
@NonCPS
|
||||
protected static Telemetry getInstance(){
|
||||
if(!instance) {
|
||||
instance = new Telemetry()
|
||||
|
||||
registerListener({ steps, payload ->
|
||||
piperOsDefaultReporting(steps, payload)
|
||||
})
|
||||
}
|
||||
return instance
|
||||
}
|
||||
|
||||
static void registerListener(Closure listener){
|
||||
getInstance().listenerList.add(listener)
|
||||
}
|
||||
|
||||
static notify(Script steps, Map config, Map payload){
|
||||
//allow opt-out via configuration
|
||||
if (!config?.collectTelemetryData) {
|
||||
steps.echo "[${payload.step}] Telemetry reporting disabled!"
|
||||
return
|
||||
}
|
||||
|
||||
getInstance().listenerList.each { listener ->
|
||||
try {
|
||||
listener(steps, payload)
|
||||
} catch (ignore) {
|
||||
// some error occured in telemetry reporting. This should not break anything though.
|
||||
steps.echo "[${payload.step}] Telemetry Report with listener failed: ${ignore.getMessage()}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static void piperOsDefaultReporting(Script steps, Map payload) {
|
||||
try {
|
||||
|
||||
def swaCustom = [:]
|
||||
|
||||
/* SWA custom parameters:
|
||||
custom3 = step name (passed as parameter step)
|
||||
custom4 = job url hashed (calculated)
|
||||
custom5 = build url hashed (calculated)
|
||||
custom10 = stage name
|
||||
custom11 = step related parameter 1 (passed as parameter stepParam1)
|
||||
custom12 = step related parameter 2 (passed as parameter stepParam2)
|
||||
custom13 = step related parameter 3 (passed as parameter stepParam3)
|
||||
custom14 = step related parameter 4 (passed as parameter stepParam4)
|
||||
custom15 = step related parameter 5 (passed as parameter stepParam5)
|
||||
*/
|
||||
|
||||
def swaUrl = 'https://webanalytics.cfapps.eu10.hana.ondemand.com/tracker/log'
|
||||
def idsite = '827e8025-1e21-ae84-c3a3-3f62b70b0130'
|
||||
def url = 'https://github.com/SAP/jenkins-library'
|
||||
|
||||
swaCustom.custom3 = payload.step
|
||||
swaCustom.custom4 = payload.jobUrlSha1
|
||||
swaCustom.custom5 = payload.buildUrlSha1
|
||||
swaCustom.custom10 = payload.stageName
|
||||
swaCustom.custom11 = payload.stepParam1
|
||||
swaCustom.custom12 = payload.stepParam2
|
||||
swaCustom.custom13 = payload.stepParam3
|
||||
swaCustom.custom14 = payload.stepParam4
|
||||
swaCustom.custom15 = payload.stepParam5
|
||||
|
||||
def options = []
|
||||
options.push("-G")
|
||||
options.push("-v \"${swaUrl}\"")
|
||||
options.push("--data-urlencode \"action_name=${payload.actionName}\"")
|
||||
options.push("--data-urlencode \"idsite=${idsite}\"")
|
||||
options.push("--data-urlencode \"url=${url}\"")
|
||||
options.push("--data-urlencode \"event_type=${payload.eventType}\"")
|
||||
for(def key : ['custom3', 'custom4', 'custom5', 'custom10', 'custom11', 'custom12', 'custom13', 'custom14', 'custom15']){
|
||||
if (swaCustom[key] != null) options.push("--data-urlencode \"${key}=${swaCustom[key]}\"")
|
||||
}
|
||||
options.push("--connect-timeout 5")
|
||||
options.push("--max-time 20")
|
||||
|
||||
steps.sh(returnStatus: true, script: "#!/bin/sh +x\ncurl ${options.join(' ')} > /dev/null 2>&1 || echo '[${payload.step}] Telemetry Report to SWA failed!'")
|
||||
|
||||
} catch (MissingContextVariableException noNode) {
|
||||
steps.echo "[${payload.step}] Telemetry Report to SWA skipped, no node available!"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.sap.piper
|
||||
|
||||
import org.junit.Rule
|
||||
import org.junit.Before
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.assertThat
|
||||
import org.junit.rules.ExpectedException
|
||||
@@ -63,38 +64,10 @@ class UtilsTest extends BasePiperTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSWAReporting() {
|
||||
utils.env = [BUILD_URL: 'something', JOB_URL: 'nothing']
|
||||
utils.pushToSWA([step: 'anything'], [collectTelemetryData: true])
|
||||
void testGenerateSHA1() {
|
||||
def result = utils.generateSha1('ContinuousDelivery')
|
||||
// asserts
|
||||
assertThat(shellRule.shell, hasItem(containsString('curl -G -v "https://webanalytics.cfapps.eu10.hana.ondemand.com/tracker/log"')))
|
||||
assertThat(shellRule.shell, hasItem(containsString('action_name=Piper Library OS')))
|
||||
assertThat(shellRule.shell, hasItem(containsString('custom3=anything')))
|
||||
assertThat(shellRule.shell, hasItem(containsString('custom5=`echo -n \'something\' | sha1sum | sed \'s/ -//\'`')))
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDisabledSWAReporting() {
|
||||
utils.env = [BUILD_URL: 'something', JOB_URL: 'nothing']
|
||||
utils.pushToSWA([step: 'anything'], [collectTelemetryData: false])
|
||||
// asserts
|
||||
assertThat(loggingRule.log, containsString('[anything] Telemetry Report to SWA disabled!'))
|
||||
assertThat(shellRule.shell, not(hasItem(containsString('https://webanalytics.cfapps.eu10.hana.ondemand.com'))))
|
||||
}
|
||||
|
||||
@Test
|
||||
void testImplicitlyDisabledSWAReporting() {
|
||||
utils.env = [BUILD_URL: 'something', JOB_URL: 'nothing']
|
||||
utils.pushToSWA([step: 'anything'], null)
|
||||
// asserts
|
||||
assertThat(loggingRule.log, containsString('[anything] Telemetry Report to SWA disabled!'))
|
||||
}
|
||||
|
||||
@Test
|
||||
void testImplicitlyDisabledSWAReporting2() {
|
||||
utils.env = [BUILD_URL: 'something', JOB_URL: 'nothing']
|
||||
utils.pushToSWA([step: 'anything'], [:])
|
||||
// asserts
|
||||
assertThat(loggingRule.log, containsString('[anything] Telemetry Report to SWA disabled!'))
|
||||
// generated with "echo -n 'ContinuousDelivery' | sha1sum | sed 's/ -//'"
|
||||
assertThat(result, is('0dad6c33b6246702132454f604dee80740f399ad'))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
package com.sap.piper.analytics
|
||||
|
||||
import org.junit.Rule
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import static org.junit.Assert.assertThat
|
||||
import static org.junit.Assume.assumeThat
|
||||
import org.junit.rules.ExpectedException
|
||||
import org.junit.rules.RuleChain
|
||||
|
||||
import static org.hamcrest.Matchers.containsString
|
||||
import static org.hamcrest.Matchers.hasItem
|
||||
import static org.hamcrest.Matchers.is
|
||||
import static org.hamcrest.Matchers.not
|
||||
import static org.hamcrest.Matchers.empty
|
||||
|
||||
import util.JenkinsLoggingRule
|
||||
import util.JenkinsShellCallRule
|
||||
import util.BasePiperTest
|
||||
import util.Rules
|
||||
|
||||
class TelemetryTest extends BasePiperTest {
|
||||
private ExpectedException thrown = ExpectedException.none()
|
||||
private JenkinsLoggingRule jlr = new JenkinsLoggingRule(this)
|
||||
private JenkinsShellCallRule jscr = new JenkinsShellCallRule(this)
|
||||
|
||||
@Rule
|
||||
public RuleChain rules = Rules
|
||||
.getCommonRules(this)
|
||||
.around(thrown)
|
||||
.around(jscr)
|
||||
.around(jlr)
|
||||
|
||||
private parameters
|
||||
|
||||
@Before
|
||||
void setup() {
|
||||
Telemetry.instance = null
|
||||
parameters = [:]
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreateInstance() {
|
||||
Telemetry.instance = new Telemetry()
|
||||
// asserts
|
||||
assertThat(Telemetry.getInstance().listenerList, is(empty()))
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetInstance() {
|
||||
// asserts
|
||||
assertThat(Telemetry.getInstance().listenerList, is(not(empty())))
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegisterListenerAndNotify() {
|
||||
// prepare
|
||||
Map notificationPayload = [:]
|
||||
Telemetry.instance = new Telemetry()
|
||||
assumeThat(Telemetry.getInstance().listenerList, is(empty()))
|
||||
|
||||
Telemetry.registerListener({ steps, payload ->
|
||||
notificationPayload = payload
|
||||
})
|
||||
// test
|
||||
Telemetry.notify(nullScript, [collectTelemetryData: true], [step: 'anyStep', anything: 'something'])
|
||||
// asserts
|
||||
assertThat(Telemetry.getInstance().listenerList, is(not(empty())))
|
||||
assertThat(notificationPayload, is([step: 'anyStep', anything: 'something']))
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNotifyWithOptOut() {
|
||||
// prepare
|
||||
Map notificationPayload = [:]
|
||||
Telemetry.instance = new Telemetry()
|
||||
assumeThat(Telemetry.getInstance().listenerList, is(empty()))
|
||||
Telemetry.registerListener({ steps, payload ->
|
||||
notificationPayload = payload
|
||||
})
|
||||
// test
|
||||
Telemetry.notify(nullScript, [collectTelemetryData: false], [step: 'anyStep', anything: 'something'])
|
||||
// asserts
|
||||
assertThat(Telemetry.getInstance().listenerList, is(not(empty())))
|
||||
assertThat(jlr.log, containsString("[anyStep] Telemetry reporting disabled!"))
|
||||
assertThat(notificationPayload.keySet(), is(empty()))
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNotifyWithOptOutWithEmptyConfig() {
|
||||
// prepare
|
||||
Map notificationPayload = [:]
|
||||
Telemetry.instance = new Telemetry()
|
||||
assumeThat(Telemetry.getInstance().listenerList, is(empty()))
|
||||
Telemetry.registerListener({ steps, payload ->
|
||||
notificationPayload = payload
|
||||
})
|
||||
// test
|
||||
Telemetry.notify(nullScript, [:], [step: 'anyStep', anything: 'something'])
|
||||
// asserts
|
||||
assertThat(Telemetry.getInstance().listenerList, is(not(empty())))
|
||||
assertThat(jlr.log, containsString("[anyStep] Telemetry reporting disabled!"))
|
||||
assertThat(notificationPayload.keySet(), is(empty()))
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNotifyWithOptOutWithoutConfig() {
|
||||
// prepare
|
||||
Map notificationPayload = [:]
|
||||
Telemetry.instance = new Telemetry()
|
||||
assumeThat(Telemetry.getInstance().listenerList, is(empty()))
|
||||
Telemetry.registerListener({ steps, payload ->
|
||||
notificationPayload = payload
|
||||
})
|
||||
// test
|
||||
Telemetry.notify(nullScript, null, [step: 'anyStep', anything: 'something'])
|
||||
// asserts
|
||||
assertThat(Telemetry.getInstance().listenerList, is(not(empty())))
|
||||
assertThat(jlr.log, containsString("[anyStep] Telemetry reporting disabled!"))
|
||||
assertThat(notificationPayload.keySet(), is(empty()))
|
||||
}
|
||||
|
||||
@Test
|
||||
void testReportingToSWA() {
|
||||
// prepare
|
||||
assumeThat(Telemetry.getInstance().listenerList, is(not(empty())))
|
||||
// test
|
||||
Telemetry.notify(nullScript, [collectTelemetryData: true], [
|
||||
actionName: 'Piper Library OS',
|
||||
eventType: 'library-os',
|
||||
jobUrlSha1: '1234',
|
||||
buildUrlSha1: 'abcd',
|
||||
step: 'anyStep',
|
||||
stepParam1: 'something'
|
||||
])
|
||||
// asserts
|
||||
assertThat(jscr.shell, hasItem(containsString('curl -G -v "https://webanalytics.cfapps.eu10.hana.ondemand.com/tracker/log"')))
|
||||
assertThat(jscr.shell, hasItem(containsString('--data-urlencode "action_name=Piper Library OS"')))
|
||||
assertThat(jscr.shell, hasItem(containsString('--data-urlencode "event_type=library-os"')))
|
||||
assertThat(jscr.shell, hasItem(containsString('--data-urlencode "custom3=anyStep"')))
|
||||
assertThat(jscr.shell, hasItem(containsString('--data-urlencode "custom4=1234"')))
|
||||
assertThat(jscr.shell, hasItem(containsString('--data-urlencode "custom5=abcd"')))
|
||||
assertThat(jscr.shell, hasItem(containsString('--data-urlencode "custom11=something"')))
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,6 @@ private void stageLocking(Map config, Closure body) {
|
||||
}
|
||||
|
||||
private void executeStage(script, originalStage, stageName, config, utils) {
|
||||
|
||||
boolean projectExtensions
|
||||
boolean globalExtensions
|
||||
def startTime = System.currentTimeMillis()
|
||||
@@ -108,14 +107,19 @@ private void executeStage(script, originalStage, stageName, config, utils) {
|
||||
stageName: stageName,
|
||||
stepParamKey1: 'buildResult',
|
||||
stepParam1: "${script.currentBuild.currentResult}",
|
||||
buildResult: "${script.currentBuild.currentResult}",
|
||||
stepParamKey2: 'stageStartTime',
|
||||
stepParam2: "${startTime}",
|
||||
stageStartTime: "${startTime}",
|
||||
stepParamKey3: 'stageDuration',
|
||||
stepParam3: "${duration}",
|
||||
stageDuration: "${duration}",
|
||||
stepParamKey4: 'projectExtension',
|
||||
stepParam4: "${projectExtensions}",
|
||||
projectExtension: "${projectExtensions}",
|
||||
stepParamKey5: 'globalExtension',
|
||||
stepParam5: "${globalExtensions}"
|
||||
stepParam5: "${globalExtensions}",
|
||||
globalExtension: "${globalExtensions}"
|
||||
], config)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user