From 7748607fc4ce45ce12bdc30964563747be879f85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Moreno=20Garc=C3=ADa?= Date: Thu, 16 Dec 2021 09:05:27 +0100 Subject: [PATCH] Add new function to send metric messages --- resources/msgBus-Metrics-Error-Content.json | 14 ++++ .../msgBus-Metrics-ExternalCall-Content.json | 44 +++++++++++ .../msgBus-Metrics-Pipeline-Content.json | 26 +++++++ .../msgBus-Metrics-RetryData-Content.json | 14 ++++ ...etrics-RetryDataConfiguration-Content.json | 32 ++++++++ resources/msgBus-Metrics-Service-Content.json | 26 +++++++ resources/msgBusMetricsMsg.json | 20 +++++ vars/msgBusMetricsErrorContent.groovy | 26 +++++++ vars/msgBusMetricsExternalCallContent.groovy | 29 +++++++ vars/msgBusMetricsMsg.groovy | 30 ++++++++ vars/msgBusMetricsPipelineContent.groovy | 26 +++++++ ...etricsRetryDataConfigurationContent.groovy | 26 +++++++ vars/msgBusMetricsRetryDataContent.groovy | 26 +++++++ vars/msgBusMetricsServiceContent.groovy | 26 +++++++ vars/sendMetricsMsg.groovy | 76 +++++++++++++++++++ 15 files changed, 441 insertions(+) create mode 100644 resources/msgBus-Metrics-Error-Content.json create mode 100644 resources/msgBus-Metrics-ExternalCall-Content.json create mode 100644 resources/msgBus-Metrics-Pipeline-Content.json create mode 100644 resources/msgBus-Metrics-RetryData-Content.json create mode 100644 resources/msgBus-Metrics-RetryDataConfiguration-Content.json create mode 100644 resources/msgBus-Metrics-Service-Content.json create mode 100644 resources/msgBusMetricsMsg.json create mode 100644 vars/msgBusMetricsErrorContent.groovy create mode 100644 vars/msgBusMetricsExternalCallContent.groovy create mode 100644 vars/msgBusMetricsMsg.groovy create mode 100644 vars/msgBusMetricsPipelineContent.groovy create mode 100644 vars/msgBusMetricsRetryDataConfigurationContent.groovy create mode 100644 vars/msgBusMetricsRetryDataContent.groovy create mode 100644 vars/msgBusMetricsServiceContent.groovy create mode 100644 vars/sendMetricsMsg.groovy diff --git a/resources/msgBus-Metrics-Error-Content.json b/resources/msgBus-Metrics-Error-Content.json new file mode 100644 index 0000000..624801e --- /dev/null +++ b/resources/msgBus-Metrics-Error-Content.json @@ -0,0 +1,14 @@ +{ + "code": { + "value": 0, + "type": "java.math.BigInteger", + "description": "Error code.", + "required": true + }, + "message": { + "value": null, + "type": "java.lang.String", + "description": "Error message.", + "required": false + } +} diff --git a/resources/msgBus-Metrics-ExternalCall-Content.json b/resources/msgBus-Metrics-ExternalCall-Content.json new file mode 100644 index 0000000..cfbff48 --- /dev/null +++ b/resources/msgBus-Metrics-ExternalCall-Content.json @@ -0,0 +1,44 @@ +{ + "service": { + "value": {}, + "type": "groovy.lang.Closure", + "description": "The service content Map.", + "required": true + }, + "source": { + "value": null, + "type": "java.lang.String", + "description": "Tool/component making the request (eg. midstream, rhprod, service, ...).", + "required": true + }, + "success": { + "value": true, + "type": "java.lang.Boolean", + "description": "Whether the call succeeded or not.", + "required": true + }, + "error": { + "value": null, + "type": "groovy.lang.Closure", + "description": "The error content Map.", + "required": false + }, + "start": { + "value": null, + "type": "java.lang.String", + "description": "Time at which the request started. (ISO-8601).", + "required": true + }, + "end": { + "value": null, + "type": "java.lang.String", + "description": "Time at which the final answer was collected (last call or retry with a proper answer) (ISO-8601).", + "required": true + }, + "retryData": { + "value": {}, + "type": "groovy.lang.Closure", + "description": "The retryData content Map.", + "required": true + } +} diff --git a/resources/msgBus-Metrics-Pipeline-Content.json b/resources/msgBus-Metrics-Pipeline-Content.json new file mode 100644 index 0000000..1212c05 --- /dev/null +++ b/resources/msgBus-Metrics-Pipeline-Content.json @@ -0,0 +1,26 @@ +{ + "id": { + "value": null, + "type": "java.lang.String", + "description": "Unique identification string. Useful for constructing tree of rebuilds of the same artifact in the Web UI. Note that it is up to the CI system to implement. Replaces thread_id from previous versions of the spec. The value must be unique among all systems reporting on the artifact; it is advised to prefix it with a unique string, e.g. the name of the CI system, etc.", + "required": true + }, + "name": { + "value": null, + "type": "java.lang.String", + "description": "A human readable name of the pipeline.", + "required": true + }, + "jenkinsUrl": { + "value": null, + "type": "java.lang.String", + "description": "The jenkins URL.", + "required": true + }, + "productId": { + "value": null, + "type": "java.lang.String", + "description": "The product id.", + "required": false + } +} diff --git a/resources/msgBus-Metrics-RetryData-Content.json b/resources/msgBus-Metrics-RetryData-Content.json new file mode 100644 index 0000000..d4a4001 --- /dev/null +++ b/resources/msgBus-Metrics-RetryData-Content.json @@ -0,0 +1,14 @@ +{ + "configuration": { + "value": {}, + "type": "groovy.lang.Closure", + "description": "The RetryDataConfiguration content Map.", + "required": false + }, + "iterations": { + "value": 0, + "type": "java.math.BigInteger", + "description": "Number of iterations (ie. number of retries plus 1 for the first call).", + "required": true + } +} diff --git a/resources/msgBus-Metrics-RetryDataConfiguration-Content.json b/resources/msgBus-Metrics-RetryDataConfiguration-Content.json new file mode 100644 index 0000000..b121fab --- /dev/null +++ b/resources/msgBus-Metrics-RetryDataConfiguration-Content.json @@ -0,0 +1,32 @@ +{ + "initialRetryInterval": { + "value": 0, + "type": "java.math.BigInteger", + "description": "Initial retry interval (first retry interval) in seconds.", + "required": false + }, + "maxElapsedTime": { + "value": 0, + "type": "java.math.BigInteger", + "description": "Maximum number of seconds for the whole retry process.", + "required": false + }, + "maxInterval": { + "value": 0, + "type": "java.math.BigInteger", + "description": "Maximum number of seconds between retries.", + "required": false + }, + "maxRetries": { + "value": 0, + "type": "java.math.BigInteger", + "description": "Maximum number of retries.", + "required": false + }, + "timeout": { + "value": 0, + "type": "java.math.BigInteger", + "description": "Timeout used for each call to the external service.", + "required": false + } +} diff --git a/resources/msgBus-Metrics-Service-Content.json b/resources/msgBus-Metrics-Service-Content.json new file mode 100644 index 0000000..1ae7ed9 --- /dev/null +++ b/resources/msgBus-Metrics-Service-Content.json @@ -0,0 +1,26 @@ +{ + "name": { + "value": null, + "type": "java.lang.String", + "description": "Name of the external service.", + "required": true + }, + "type": { + "value": null, + "type": "java.lang.String", + "description": "Type of call (ie. cli or http).", + "required": true + }, + "server": { + "value": null, + "type": "java.lang.String", + "description": "Server contacted in case of not targeting the prod server.", + "required": false + }, + "endpoint": { + "value": null, + "type": "java.lang.String", + "description": "Either http endpoint or cli subcommand.", + "required": false + } +} diff --git a/resources/msgBusMetricsMsg.json b/resources/msgBusMetricsMsg.json new file mode 100644 index 0000000..0050f53 --- /dev/null +++ b/resources/msgBusMetricsMsg.json @@ -0,0 +1,20 @@ +{ + "externalCall": { + "value": {}, + "type": "groovy.lang.Closure", + "description": "The externalCall content Map.", + "required": true + }, + "generated_at": { + "value": null, + "type": "java.lang.String", + "description": "Timestamp of message creation (ISO-8601).", + "required": true + }, + "pipeline": { + "value": {}, + "type": "groovy.lang.Closure", + "description": "The pipeline content Map.", + "required": true + } +} diff --git a/vars/msgBusMetricsErrorContent.groovy b/vars/msgBusMetricsErrorContent.groovy new file mode 100644 index 0000000..6a3ffcf --- /dev/null +++ b/vars/msgBusMetricsErrorContent.groovy @@ -0,0 +1,26 @@ +import org.centos.contra.pipeline.Utils + +/** + * Defines the error content of an external call metrics message + * This will merge parameters with the defaults and will validate each parameter + * @param parameters + * @return HashMap + */ +def call(Map parameters = [:]) { + + def utils = new Utils() + + def defaults = readJSON text: libraryResource('msgBus-Metrics-Error-Content.json') + + return { Map runtimeArgs = [:] -> + parameters = utils.mapMergeQuotes([parameters, runtimeArgs]) + try { + mergedMessage = utils.mergeBusMessage(parameters, defaults) + } catch(e) { + throw new Exception("Creating closure for external call metric error content failed: " + e) + } + + // sendCIMessage expects String arguments + return utils.getMapStringColon(mergedMessage) + } +} diff --git a/vars/msgBusMetricsExternalCallContent.groovy b/vars/msgBusMetricsExternalCallContent.groovy new file mode 100644 index 0000000..5959c6b --- /dev/null +++ b/vars/msgBusMetricsExternalCallContent.groovy @@ -0,0 +1,29 @@ +import org.centos.contra.pipeline.Utils + +/** + * Defines the external call content of a metrics message + * This will merge parameters with the defaults and will validate each parameter + * @param parameters + * @return HashMap + */ +def call(Map parameters = [:]) { + + def utils = new Utils() + + def defaults = readJSON text: libraryResource('msgBus-Metrics-ExternalCall-Content.json') + + return { Map runtimeArgs = [:] -> + parameters['service'] = parameters['service'] ?: msgBusMetricsServiceContent()() + parameters['retryData'] = parameters['retryData'] ?: msgBusMetricsRetryDataContent()() + + parameters = utils.mapMergeQuotes([parameters, runtimeArgs]) + try { + mergedMessage = utils.mergeBusMessage(parameters, defaults) + } catch(e) { + throw new Exception("Creating closure for external call content failed: " + e) + } + + // sendCIMessage expects String arguments + return utils.getMapStringColon(mergedMessage) + } +} diff --git a/vars/msgBusMetricsMsg.groovy b/vars/msgBusMetricsMsg.groovy new file mode 100644 index 0000000..31ed426 --- /dev/null +++ b/vars/msgBusMetricsMsg.groovy @@ -0,0 +1,30 @@ +import org.centos.contra.pipeline.Utils + +/** + * Defines the metrics message + * This will merge parameters with the defaults and will validate each parameter + * @param parameters + * @return HashMap + */ +def call(Map parameters = [:]) { + + def utils = new Utils() + + def defaults = readJSON text: libraryResource('msgBusMetricsMsg.json') + + return { Map runtimeArgs = [:] -> + parameters['externalCall'] = parameters['externalCall'] ?: msgBusMetricsExternalCallContent()() + parameters['generated_at'] = parameters['generated_at'] ?: java.time.Instant.now().toString() + parameters['pipeline'] = parameters['pipeline'] ?: msgBusMetricsPipelineContent()() + + parameters = utils.mapMergeQuotes([parameters, runtimeArgs]) + try { + mergedMessage = utils.mergeBusMessage(parameters, defaults) + } catch(e) { + throw new Exception("Creating the metrics message failed: " + e) + } + + // sendCIMessage expects String arguments + return utils.getMapStringColon(mergedMessage) + } +} diff --git a/vars/msgBusMetricsPipelineContent.groovy b/vars/msgBusMetricsPipelineContent.groovy new file mode 100644 index 0000000..3e79890 --- /dev/null +++ b/vars/msgBusMetricsPipelineContent.groovy @@ -0,0 +1,26 @@ +import org.centos.contra.pipeline.Utils + +/** + * Defines the pipeline content of a metrics message + * This will merge parameters with the defaults and will validate each parameter + * @param parameters + * @return HashMap + */ +def call(Map parameters = [:]) { + + def utils = new Utils() + + def defaults = readJSON text: libraryResource('msgBus-Metrics-Pipeline-Content.json') + + return { Map runtimeArgs = [:] -> + parameters = utils.mapMergeQuotes([parameters, runtimeArgs]) + try { + mergedMessage = utils.mergeBusMessage(parameters, defaults) + } catch(e) { + throw new Exception("Creating closure for metric pipeline content failed: " + e) + } + + // sendCIMessage expects String arguments + return utils.getMapStringColon(mergedMessage) + } +} diff --git a/vars/msgBusMetricsRetryDataConfigurationContent.groovy b/vars/msgBusMetricsRetryDataConfigurationContent.groovy new file mode 100644 index 0000000..dd9d952 --- /dev/null +++ b/vars/msgBusMetricsRetryDataConfigurationContent.groovy @@ -0,0 +1,26 @@ +import org.centos.contra.pipeline.Utils + +/** + * Defines the configuration content of a retry closure in an external call metrics message + * This will merge parameters with the defaults and will validate each parameter + * @param parameters + * @return HashMap + */ +def call(Map parameters = [:]) { + + def utils = new Utils() + + def defaults = readJSON text: libraryResource('msgBus-Metrics-RetryDataConfiguration-Content.json') + + return { Map runtimeArgs = [:] -> + parameters = utils.mapMergeQuotes([parameters, runtimeArgs]) + try { + mergedMessage = utils.mergeBusMessage(parameters, defaults) + } catch(e) { + throw new Exception("Creating the retry configuration closure for retry metrics message failed: " + e) + } + + // sendCIMessage expects String arguments + return utils.getMapStringColon(mergedMessage) + } +} diff --git a/vars/msgBusMetricsRetryDataContent.groovy b/vars/msgBusMetricsRetryDataContent.groovy new file mode 100644 index 0000000..669aa28 --- /dev/null +++ b/vars/msgBusMetricsRetryDataContent.groovy @@ -0,0 +1,26 @@ +import org.centos.contra.pipeline.Utils + +/** + * Defines the retry content of a retry metrics message + * This will merge parameters with the defaults and will validate each parameter + * @param parameters + * @return HashMap + */ +def call(Map parameters = [:]) { + + def utils = new Utils() + + def defaults = readJSON text: libraryResource('msgBus-Metrics-RetryData-Content.json') + + return { Map runtimeArgs = [:] -> + parameters = utils.mapMergeQuotes([parameters, runtimeArgs]) + try { + mergedMessage = utils.mergeBusMessage(parameters, defaults) + } catch(e) { + throw new Exception("Creating retryData closure for metrics message failed: " + e) + } + + // sendCIMessage expects String arguments + return utils.getMapStringColon(mergedMessage) + } +} diff --git a/vars/msgBusMetricsServiceContent.groovy b/vars/msgBusMetricsServiceContent.groovy new file mode 100644 index 0000000..0116d46 --- /dev/null +++ b/vars/msgBusMetricsServiceContent.groovy @@ -0,0 +1,26 @@ +import org.centos.contra.pipeline.Utils + +/** + * Defines the service content of an external call metrics message + * This will merge parameters with the defaults and will validate each parameter + * @param parameters + * @return HashMap + */ +def call(Map parameters = [:]) { + + def utils = new Utils() + + def defaults = readJSON text: libraryResource('msgBus-Metrics-Service-Content.json') + + return { Map runtimeArgs = [:] -> + parameters = utils.mapMergeQuotes([parameters, runtimeArgs]) + try { + mergedMessage = utils.mergeBusMessage(parameters, defaults) + } catch(e) { + throw new Exception("Creating closure for external call metric service content failed: " + e) + } + + // sendCIMessage expects String arguments + return utils.getMapStringColon(mergedMessage) + } +} diff --git a/vars/sendMetricsMsg.groovy b/vars/sendMetricsMsg.groovy new file mode 100644 index 0000000..4813b6c --- /dev/null +++ b/vars/sendMetricsMsg.groovy @@ -0,0 +1,76 @@ +import org.centos.contra.pipeline.Utils + +/** + * requires: env.topicPrefix + * optional: env.msgProperties: This optional variable can be defined to pass along custom message headers in the messages sent by sendPipelineStatusMsg + * Example Usage: + * msgProperties = ['service': {}, 'retryData': {}, ...] + * sendMetricsMsg(messageProperties) { + * } + * + * @param metricsMap: A map containing the metrics to be sent over the bus + * @return + */ + +def call(Map metricsMap) { + // Make sure required env variables are set. The ones used in the + // message bodies are enforced by the json closures. + // Note: Error message should be changed if variables are added here + if (!env.topicPrefix) { + error("Missing env.topicPrefix required variable to use sendMetricsMsg") + } + + def msgTopic = env.topicPrefix + ".pipeline.metrics" + + try { + service = msgBusMetricsServiceContent( + metricsMap['service'] + ) + error = metricsMap['error'] ? msgBusMetricsErrorContent(code: metricsMap['error']['code'], message: metricsMap['error']['message']) : null + + def retryData + if (metricsMap['retryData']['configuration']) { + retryDataConfiguration = msgBusMetricsRetryDataConfigurationContent( + metricsMap['retryData']['configuration'] + ) + retryData = msgBusMetricsRetryDataContent(configuration: retryDataConfiguration(), iterations: metricsMap['retryData']['iterations']) + } else { + retryData = msgBusMetricsRetryDataContent(iterations: metricsMap['retryData']['iterations']) + } + + externalCall = metricsMap['error'] ? msgBusMetricsExternalCallContent( + service: service(), + source: metricsMap['source'], + success: metricsMap['success'], + error: error(), + start: metricsMap['start'], + end: metricsMap['end'], + retryData: retryData() + ) : msgBusMetricsExternalCallContent( + service: service(), + source: metricsMap['source'], + success: metricsMap['success'], + start: metricsMap['start'], + end: metricsMap['end'], + retryData: retryData() + ) + pipeline = env.productId ? msgBusMetricsPipelineContent( + id: env.pipelineId, + name: env.pipelineName, + jenkinsUrl: env.JENKINS_URL, + productId: env.productId + ) : msgBusMetricsPipelineContent( + id: env.pipelineId, + name: env.pipelineName, + jenkinsUrl: env.JENKINS_URL + ) + metricsMsg = msgBusMetricsMsg(externalCall: externalCall(), pipeline: pipeline()) + + // Send message + def utils = new Utils() + utils.sendMessage(msgTopic, env.msgProperties ?: "", metricsMsg()) + + } catch(e) { + println("No message was sent out on topic " + msgTopic + ". The error encountered was: " + e) + } +}