diff --git a/.gitignore b/.gitignore index 6a595d6..25a44bd 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,22 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* -.idea/* + target/* */target/* -*.DS_Store +bin/ + +# MacOS +.DS_Store + +# IntelliJ +*.iml +.idea/* + +# VS Code +.project +.settings/ +.vscode/ + +.classpath +.factorypath diff --git a/OverOps.iml b/OverOps.iml deleted file mode 100644 index 736e3f3..0000000 --- a/OverOps.iml +++ /dev/null @@ -1,386 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/README.md b/README.md index 7344306..794773f 100644 --- a/README.md +++ b/README.md @@ -1,236 +1,176 @@ -# Bamboo OverOps Quality Gates Plugin - - ##### 1. Install Atlassian SDK - * Mac OS (HomeBrew) - 1. Open a Terminal window and add the Atlassian "Tap" to your Brew using the command: -
`brew tap atlassian/tap` - 2. Then install the SDK using the atlassian/tap command: -
`brew install atlassian/tap/atlassian-plugin-sdk` - 3. [Next: Verify that you have set up the SDK correctly](https://developer.atlassian.com/display/DOCS/Install+the+Atlassian+SDK+on+a+Linux+or+Mac+system#InstalltheAtlassianSDKonaLinuxorMacsystem-step3Step3:VerifythatyouhavesetuptheSDKcorrectly) - - * Debian, Ubuntu Linux - 1. First, set up the Atlassian SDK repositories: -
`sudo sh -c 'echo "deb https://packages.atlassian.com/debian/atlassian-sdk-deb/ stable contrib" >>/etc/apt/sources.list'` - 2. Download the public key using curl or wget: -
`wget https://packages.atlassian.com/api/gpg/key/public` - 3. Add the public key to apt to verify the package signatures automatically: -
`sudo apt-key add public ` - 4. Then, run the install: -
`sudo apt-get update` -
`sudo apt-get install atlassian-plugin-sdk` - 6. [Next: Verify that you have set up the SDK correctly](https://developer.atlassian.com/display/DOCS/Install+the+Atlassian+SDK+on+a+Linux+or+Mac+system#InstalltheAtlassianSDKonaLinuxorMacsystem-step3Step3:VerifythatyouhavesetuptheSDKcorrectly) - - * Red Hat Enterprise Linux, CentOS, Fedora (RPM) - - To install on systems that use the Yum package manager: - - 1. Create the repo file in your /etc/yum.repos.d/ folder: -
`sudo vi /etc/yum.repos.d/artifactory.repo` - 2. Configure the repository details: -
`[Artifactory]` -
`name=Artifactory` -
`baseurl=https://packages.atlassian.com/yum/atlassian-sdk-rpm/` -
`enabled=1` -
`gpgcheck=0` - 3. Install the SDK: -
`sudo yum clean all` -
`sudo yum updateinfo metadata` -
`sudo yum install atlassian-plugin-sdk` - 4. [Next: Verify that you have set up the SDK correctly](https://developer.atlassian.com/display/DOCS/Install+the+Atlassian+SDK+on+a+Linux+or+Mac+system#InstalltheAtlassianSDKonaLinuxorMacsystem-step3Step3:VerifythatyouhavesetuptheSDKcorrectly) - - * .tgz File - 1. [Download a TGZ (GZipped tar file) of the SDK](https://marketplace.atlassian.com/download/plugins/atlassian-plugin-sdk-tgz?_ga=2.135130865.1342090517.1563909899-484610990.1554922063) - 2. Locate the downloaded SDK file. - 3. Extract the file to your local directory. -
`sudo tar -xvzf atlassian-plugin-sdk-4.0.tar.gz -C /opt` - 4. Rename the extracted folder to atlassian-plugin-sdk . -
`sudo mv /opt/atlassian-plugin-sdk-4.0 /opt/atlassian-plugin-sdk ` - 5. [Next: Verify that you have set up the SDK correctly](https://developer.atlassian.com/display/DOCS/Install+the+Atlassian+SDK+on+a+Linux+or+Mac+system#InstalltheAtlassianSDKonaLinuxorMacsystem-step3Step3:VerifythatyouhavesetuptheSDKcorrectly) - - ##### 2. Verify that you have set up the SDK correctly - * Open a terminal window and run the following command: -
`atlas-version` -
`ATLAS Version: 6.2.9` -
`ATLAS Home: /usr/local/Cellar/atlassian-plugin-sdk/6.2.4/libexec` -
`ATLAS Scripts: /usr/local/Cellar/atlassian-plugin-sdk/6.2.4/libexec/bin` -
`ATLAS Maven Home: /usr/local/Cellar/atlassian-plugin-sdk/6.2.4/libexec/apache-maven-3.2.1` -
`AMPS Version: 6.2.6` -
`--------` -
`Executing: /usr/local/Cellar/atlassian-plugin-sdk/6.2.4/libexec/apache-maven-3.2.1/bin/mvn --version -gs /usr/local/Cellar/atlassian-plugin-sdk/6.2.4/libexec/apache-maven-3.2.1/conf/settings.xml` -
`Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256M; support was removed in 8.0` -
`Apache Maven 3.2.1 (ea8b2b07643dbb1b84b6d16e1f08391b666bc1e9; 2014-02-15T04:37:52+10:00)` -
`Maven home: /usr/local/Cellar/atlassian-plugin-sdk/6.2.4/libexec/apache-maven-3.2.1` -
`Java version: 1.8.0_45, vendor: Oracle Corporation` -
`Java home: /Library/Java/JavaVirtualMachines/jdk1.8.0_45.jdk/Contents/Home/jre` -
`Default locale: en_US, platform encoding: UTF-8` -
`OS name: "mac os x", version: "10.11.6", arch: "x86_64", family: "mac"` - - ##### 3. NOTICE - * in the future use [`atlas-mvn`](https://developer.atlassian.com/server/framework/atlassian-sdk/atlas-mvn/) instead of `mvn` -
`atlas-mvn [options]` -
`atlas-mvn -help` -
`atlas-mvn clean package` - - ## Setup Bamboo OverOps Quality Gates Plugin - ### 1. Global Configuration -![configure system](readme/config.png) - -##### OverOps URL +# OverOps Quality Report - Bamboo App -The complete URL of the OverOps API, including port. https://api.overops.com for SaaS or http://host.domain.com:8080 for on prem. +This app provides a mechanism for applying OverOps severity assignment and regression analysis to new builds to allow application owners, DevOps engineers, and SREs to determine the quality of their code before promoting it into production. -##### OverOps Environment ID +Run this app as a post build step after all other testing is complete to generate a Quality Report that will determine the stability of the build. From the Quality Report you can drill down into each specific error using the OverOps [Automated Root Cause](https://doc.overops.com/docs/automated-root-cause-arc) analysis screen to solve the issue. -The default OverOps environment identifier (e.g. S12345) if none is specified in the build settings. Make sure the "S" is capitalized. +For more information about this app and [quality gates](https://doc.overops.com/docs/overops-quality-gates), see the [OverOps CI/CD Pipeline Integrations guide](https://doc.overops.com/docs/cicd-pipeline). -##### OverOps API Token +## Installation -The OverOps REST API token to use for authentication. This can be obtained from the OverOps dashboard under Settings → Account. - ### 2. Setup build step -![configure system](readme/step.png) +Navigate to Bamboo Administration → Manage apps. Click "Find New Apps" and search for "OverOps" to install the app from the Atlassian Marketplace. Alternatively, you may download the [latest release](https://github.com/takipi/bamboo-overops-plugin/releases) and click "Upload app" to install the app directly. -#### Application Name +## Global Configuration -**Example:** ${bamboo.shortJobName} +After installing the app, configure it to connect to OverOps. -*(Optional)* [Application Name](https://doc.overops.com/docs/naming-your-application-server-deployment) as specified in OverOps +From Bamboo Administration, select "Settings" under "OverOps". Enter the API URL and your API Token. Optionally, you may also set the default Environment ID. -* If populated, the plugin will filter the data for the specific application in OverOps. -* If blank, no application filter will be applied in query. +![configure system](readme/config.png) -#### Deployment Name +### API URL + +The complete URL of the OverOps API, including port. For example, https://api.overops.com for SaaS or http://host.domain.com:8080 for on prem. + +### API Token + +The OverOps REST API token to use for authentication. [This can be obtained from the OverOps dashboard under Settings → Account](https://doc.overops.com/docs/api-token). + +### Environment ID + +The default OverOps environment identifier (e.g. S12345) if none is specified in the Quality Report configuration for your project. -**Example:** ${bamboo.buildNumber} or ${bamboo.shortJobName}-${bamboo.buildNumber} +#### Testing -*(Optional)* [Deployment Name](https://doc.overops.com/docs/naming-your-application-server-deployment) as specified in OverOps or use Jenkins environment variables. +Click *Test Connection* to confirm connectivity to the API server. If an Environment ID is provided, this will also confirm the credentials provided have access to the environment. -* If populated, the plugin will filter the data for the specific deployment name in OverOps -* If blank, no deployment filter will be applied in the query. +## Quality Report Configuration + +Navigate to Plan Configuration → Tasks for your Bamboo project. Click "Add task" and search for the OverOps Quality Report. + +![configure system](readme/step.png) + +### General Settings #### Environment ID -The OverOps environment identifier (e.g S4567) to inspect data for this build. If no value is provided here, the value provided in the global Jenkins plugin settings will be used. +The OverOps environment identifier (e.g S4567) If no value is provided here, the default value provided in the global configuration will be used. + +#### Application Name + +*(Optional)* [Application Name](https://doc.overops.com/docs/naming-your-application-server-deployment) as specified in OverOps. [Bamboo variables](https://confluence.atlassian.com/bamboo/bamboo-variables-289277087.html) may be used in this field. + +#### Deployment Name + +*(Optional)* [Deployment Name](https://doc.overops.com/docs/naming-your-application-server-deployment) as specified in OverOps. [Bamboo variables](https://confluence.atlassian.com/bamboo/bamboo-variables-289277087.html) may be used in this field. #### Regex Filter -A way to filter out specific event types from affecting the outcome of the OverOps Reliability report. +Filter out specific event types from the OverOps Quality Report. Event types include: *Uncaught Exception, Caught Exception, Swallowed Exception, Logged Error, Logged Warning, Timer* + +**Example:** ```"type":\"s*(Logged Error|Logged Warning|Timer)``` + +#### Number of total and unique errors to show -* Sample list of event types, Uncaught Exception, Caught Exception,|Swallowed Exception, Logged Error, Logged Warning, Timer -* This filter enables the removal of one or more of these event types from the final results. -* Example filter expression with pipe separated list- ```"type":\"s*(Logged Error|Logged Warning|Timer)``` +Prints the top X events (as provided by this parameter) with the highest volume of errors detected in the build. This is used in conjunction with the Total Error Volume Gate and the Unique Error Volume Gate to identify the errors which caused a build to fail. #### Mark Build Unstable -If checked the build will be marked unstable if any of the above gates are met. +If checked the build will be marked ***failed*** if any quality gate did not pass. -#### Show Top Issues +### Quality Gate Settings -Prints the top X events (as provided by this parameter) with the highest volume of errors detected in the current build. This is used in conjunction with Max Error Volume and Unique Error Volume to identify the errors which caused a build to fail. + #### New Error Gate -Detect all new errors in the build. If found, the build will be marked as unstable. +Detect all new errors in the build. #### Resurfaced Error Gate -Detect all resurfaced errors in the build. If found, the build will be marked as unstable. +Detect all resurfaced errors in the build. #### Total Error Volume Gate -Set the max total error volume allowed. If exceeded the build will be marked as unstable. +Set the max total error volume allowed. #### Unique Error Volume Gate -Set the max unique error volume allowed. If exceeded the build will be marked as unstable. +Set the max unique error volume allowed. #### Critical Exception Type Gate -A comma delimited list of exception types that are deemed as severe regardless of their volume. If any events of any exceptions listed have a count greater than zero, the build will be marked as unstable. +A comma delimited list of exception types that are deemed as critical. -**Example:** -NullPointerException,IndexOutOfBoundsException +**Example:** `NullPointerException,IndexOutOfBoundsException` #### Increasing Errors Gate -**Combines the following parameters:** +Detect increasing error counts in your current build versus a given baseline. -* Event Volume Threshold -* Event Rate Threshold -* Regression Delta -* Critical Regression Threshold -* Apply Seasonality +##### Active Time Window -##### Active Time Window (d - day, h - hour, m - minute) +The time window inspected to search for increasing errors. Set to zero to use the Deployment Name. Supported values are: **d** - day, **h** - hour, **m** - minute. -The time window inspected to search for new issues and regressions. To compare the current build with a baseline time window, leave the value at zero. +**Example:** `12h` -* **Example:** 1d would be one day active time window. +##### Baseline Time Window -##### Baseline Time Window (d - day, h - hour, m - minute) +The time window against which events in the active window are compared to test for increasing errors. Supported values are: **d** - day, **h** - hour, **m** - minute. -The time window against which events in the active window are compared to test for regressions. For using the Increasing Error Gate, a baseline time window is required +**Example:** `7d` -* **Example:** 14d would be a two week baseline time window. +##### Error Volume Threshold -##### Event Volume Threshold +The minimum number of times an event must take place to fail the quality gate. -The minimal number of times an event of a non-critical type (e.g. uncaught) must take place to be considered severe. +##### Error Rate Threshold (0-1) -* If a New event has a count greater than the set value, it will be evaluated as severe and could break the build if its event rate is above the Event Rate Threshold. -* If an Existing event has a count greater than the set value, it will be evaluated as severe and could break the build if its event rate is above the Event Rate Threshold and the Critical Regression Threshold. -* If any event has a count less than the set value, it will not be evaluated as severe and will not break the build. +The minimum rate at which event must take place to fail the quality gate. A rate of 0.1 means the events is allowed to take place <= 10% of the time. -##### Event Rate Threshold (0-1) +##### Regression Delta (0-1) -The minimum rate at which event of a non-critical type (e.g. uncaught) must take place to be considered severe. A rate of 0.1 means the events is allowed to take place <= 10% of the time. +The change in percentage between an event's rate in the active time window compared to the baseline to be considered a increasing error. A rate of 0.1 means the events is allowed to take place <= 10% of the time. -* If a New event has a rate greater than the set value, it will be evaluated as severe and could break the build if its event volume is above the Event Volume Threshold. -* If an Existing event has a rate greater than the set value, it will be evaluated as severe and could break the build if its event volume is above the Event Volume Threshold and the Critical Regression Threshold. -* If an event has a rate less than the set value, it will not be evaluated as severe and will not break the build. +##### Critical Regression Threshold (0-1) -##### Regression Delta (0-1) +The change in percentage between an event's rate in the active time window compared to the baseline to be considered a critical increasing error. A rate of 0.1 means the events is allowed to take place <= 10% of the time. -The change in percentage between an event's rate in the active time span compared to the baseline to be considered a regression. The active time span is the Active Time Window or the Deployment Name (whichever is populated). A rate of 0.1 means the events is allowed to take place <= 10% of the time. +##### Apply Seasonality -* If an Existing event has an error rate delta (active window compared to baseline) greater than the set value, it will be marked as a regression, but will not break the build. +If peaks have been seen in baseline window, then this would be considered normal and not an increasing error. Should OverOps identify an equal or matching peak in the baseline time window, or two peaks of greater than 50% of the volume seen in the active time window, the event will not be marked as an increasing error. -##### Critical Regression Threshold (0-1) +#### Debug -The change in percentage between an event's rate in the active time span compared to the baseline to be considered a critical regression. The active time span is the Active Time Window or the Deployment Name (whichever is populated). A rate of 0.1 means the events is allowed to take place <= 10% of the time. +If checked, all queries and results will be logged. *For debugging purposes only.* -* If an Existing event has an error rate delta (active window compared to baseline) greater than the set value, it will be marked as a severe regression and will break the build. +## Troubleshooting -##### Apply Seasonality +For short-lived applications, [we recommend](https://support.overops.com/hc/en-us/articles/360041054474-Best-Practice-Short-lived-application-considerations) using the ```-Dtakipi.shutdown.gracetime=20000``` agent property. + +## Building from source + +### Install Atlassian SDK + +Before building the app, you must install the Atlassian SDK for [Windows](https://developer.atlassian.com/server/framework/atlassian-sdk/install-the-atlassian-sdk-on-a-windows-system/), [Linux or Mac](https://developer.atlassian.com/server/framework/atlassian-sdk/install-the-atlassian-sdk-on-a-linux-or-mac-system/). Verify that the SDK has been installed using the `atlas-version` command, which produces output similar to: + +```sh +ATLAS Version: 8.0.16 +ATLAS Home: /usr/local/Cellar/atlassian-plugin-sdk/8.0.16/libexec +ATLAS Scripts: /usr/local/Cellar/atlassian-plugin-sdk/8.0.16/libexec/bin +ATLAS Maven Home: /usr/local/Cellar/atlassian-plugin-sdk/8.0.16/libexec/apache-maven-3.5.4 +AMPS Version: 8.0.2 +-------- +Executing: /usr/local/Cellar/atlassian-plugin-sdk/8.0.16/libexec/apache-maven-3.5.4/bin/mvn --version -gs /usr/local/Cellar/atlassian-plugin-sdk/8.0.16/libexec/apache-maven-3.5.4/conf/settings.xml +Apache Maven 3.5.4 (1edded0938998edf8bf061f1ceb3cfdeccf443fe; 2018-06-17T12:33:14-06:00) +Maven home: /usr/local/Cellar/atlassian-plugin-sdk/8.0.16/libexec/apache-maven-3.5.4 +Java version: 1.8.0_191, vendor: Oracle Corporation, runtime: /Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre +Default locale: en_US, platform encoding: UTF-8 +OS name: "mac os x", version: "10.14.6", arch: "x86_64", family: "mac" +``` + +### Build the app + +After cloning this project, build the app with `atlas-mvn clean package`. The resulting `jar` and `obr` files can be found in the `target/` directory. + +Deploy the app by uploading the `jar` or `obr` on the Bamboo Administration Manage apps page. + +Debug logs can be found in `/var/atlassian/application-data/bamboo/logs/atlassian-bamboo.log` + +### Developer documentation -If peaks have been seen in baseline window, then this would be considered normal and not a regression. Should the plugin identify an equal or matching peak in the baseline time window, or two peaks of greater than 50% of the volume seen in the active window, the event will not be marked as a regression. - -#### Debug Mode - -If checked, all queries and results will be displayed in the OverOps reliability report. For debugging purposes only. - - -#### Parameters - -All parameters are optional. - -| Parameter | Type | Default Value | -|---------|------|---------------| -| [`applicationName`](#application-name) | String | `null` | -| [`deploymentName`](#deployment-name) | String | `null` | -| [`serviceId`](#environment-id) | String | `null` | -| [`regexFilter`](#regex-filter) | String | `null` | -| [`markUnstable`](#mark-build-unstable) | boolean | `false` | -| [`printTopIssues`](#show-top-issues) | Integer | `5` | -| [`newEvents`](#new-error-gate) | boolean | `false` | -| [`resurfacedErrors`](#resurfaced-error-gate) | boolean | `false` | -| [`maxErrorVolume`](#total-error-volume-gate) | Integer | `0` | -| [`maxUniqueErrors`](#unique-error-volume-gate) | Integer | `0` | -|[`criticalExceptionTypes`](#critical-exception-type-gate) | String | `null` | -| [`activeTimespan`](#active-time-window-d---day-h---hour-m---minute) | String | `null` | -| [`baselineTimespan`](#baseline-time-window--d---day-h---hour-m---minute) | String | `null` | -| [`minVolumeThreshold`](#event-volume-threshold) | Integer | `0` | -| [`minErrorRateThreshold`](#event-rate-threshold-0-1) | Double | `0` | -| [`regressionDelta`](#regression-delta-0-1) | Double | `0` | -| [`criticalRegressionDelta`](#critical-regression-threshold-0-1) | Double | `0` | -| [`applySeasonality`](#apply-seasonality) | boolean | `false` | -| [`debug`](#debug-mode) | boolean | `false` | +* [`atlas-create-bamboo-plugin`](https://developer.atlassian.com/server/framework/atlassian-sdk/atlas-create-bamboo-plugin/) +* [Gettings started](https://developer.atlassian.com/server/bamboo/) +* [Bamboo plugin guide](https://developer.atlassian.com/server/bamboo/bamboo-plugin-guide/) +* [Atlassian Spring Scanner](https://bitbucket.org/atlassian/atlassian-spring-scanner/src/1.2.x/README.md?at=1.2.x&fileviewer=file-view-default) +* [Configuration of Instructions in Atlassian Plugins](https://developer.atlassian.com/server/framework/atlassian-sdk/configuration-of-instructions-in-atlassian-plugins/) diff --git a/bamboo.iml b/bamboo.iml deleted file mode 100644 index ac80e31..0000000 --- a/bamboo.iml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pom.xml b/pom.xml index c7cde9e..cda87b7 100644 --- a/pom.xml +++ b/pom.xml @@ -1,75 +1,80 @@ - + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 - - 6.2.2 - 5.7.2 - 6.3.6 - 1.2.3 - 2.23.0 - - - com.overops.plugins - bamboo - 1.0.0 - - OverOps Quality test - This plugin will check a build for errors in OverOps. - https://github.com/takipi/bamboo-overops-plugin - atlassian-plugin + overops-bamboo-app + 1.1.0 OverOps https://www.overops.com/ - - - maven-central - Maven Central - http://repo1.maven.org/maven2 - - + OverOps Quality Report + + This app provides a mechanism for applying OverOps severity assignment and regression analysis + to new builds to allow application owners, DevOps engineers, and SREs to determine the quality of + their code before promoting it into production. Run the OverOps Quality Report task after all + other testing is complete to generate a Quality Report that will determine the stability of the + build. From the Quality Report you can drill down into each specific error using the OverOps + Automated Root Cause analysis screen to solve the issue. + + https://github.com/takipi/bamboo-overops-plugin + atlassian-plugin + + + 6.8.0 + 6.8.0 + 8.0.2 + 2.0.1 + 1.2.13 + ${project.groupId}.${project.artifactId} + UTF-8 + 1.8 + 1.8 + + 2.33.0 + - - javax.xml.bind - jaxb-api - 2.1 - com.atlassian.bamboo atlassian-bamboo-web ${bamboo.version} provided - - - org.slf4j - slf4j-log4j12 - - - org.slf4j - slf4j-api - - + - ch.qos.logback - logback-classic - 1.2.3 + com.atlassian.plugin + atlassian-spring-scanner-annotation + ${atlassian.spring.scanner.version} + compile - - com.atlassian.plugins - atlassian-plugins-osgi-testrunner - ${plugin.testrunner.version} + com.atlassian.plugin + atlassian-spring-scanner-runtime + ${atlassian.spring.scanner.version} + runtime + + + + javax.inject + javax.inject + 1 + provided + + + + junit + junit + 4.10 test + com.atlassian.templaterenderer atlassian-template-renderer-api @@ -80,7 +85,7 @@ com.takipi api-client - ${version.takipi} + ${takipi.version} org.slf4j @@ -88,66 +93,102 @@ + com.takipi api-client-util - ${version.takipi} + ${takipi.version} + + + + com.takipi + report-service + ${takipi.version} + + + + + + com.atlassian.plugins + atlassian-plugins-osgi-testrunner + ${plugin.testrunner.version} + test + + + javax.ws.rs + jsr311-api + 1.1.1 + provided + + + com.google.code.gson + gson + 2.2.2-atlassian-1 + + + src/main/resources + true + + com.atlassian.maven.plugins - maven-bamboo-plugin + bamboo-maven-plugin ${amps.version} true ${bamboo.version} ${bamboo.data.version} + true + + + + + ${atlassian.plugin.key} + + + + com.overops.plugins, + + + + + org.joda.convert.*; + org.springframework.osgi.*;resolution:="optional", + org.eclipse.gemini.blueprint.*;resolution:="optional", + * + + + + * + + - org.apache.maven.plugins - maven-compiler-plugin - 3.8.1 - - 1.8 - 1.8 - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.20 - - false - - - - maven-resources-plugin - 2.6 + com.atlassian.plugin + atlassian-spring-scanner-maven-plugin + ${atlassian.spring.scanner.version} - copy-resources - - validate - copy-resources + atlassian-spring-scanner - - ${basedir}/target/classes - - - ${basedir}/src/main/resources - true - - version.properties - - - - + process-classes + + + + com.atlassian.plugin + atlassian-spring-scanner-external-jar + + + false + diff --git a/readme/config.png b/readme/config.png index 8ea676c..cac90da 100644 Binary files a/readme/config.png and b/readme/config.png differ diff --git a/readme/step.png b/readme/step.png index 146dc88..419314d 100644 Binary files a/readme/step.png and b/readme/step.png differ diff --git a/src/main/java/com/overops/plugins/bamboo/SettingsProcessor.java b/src/main/java/com/overops/plugins/bamboo/SettingsProcessor.java deleted file mode 100644 index db0cb71..0000000 --- a/src/main/java/com/overops/plugins/bamboo/SettingsProcessor.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.overops.plugins.bamboo; - -import com.atlassian.bamboo.chains.StageExecution; -import com.atlassian.bamboo.chains.plugins.PreJobAction; -import com.atlassian.bamboo.task.TaskDefinition; -import com.atlassian.bamboo.v2.build.BuildContext; -import com.atlassian.sal.api.pluginsettings.PluginSettings; -import com.atlassian.sal.api.pluginsettings.PluginSettingsFactory; -import com.overops.plugins.bamboo.configuration.Const; - -import java.util.List; -import java.util.Map; - -import org.jetbrains.annotations.NotNull; - -import static com.overops.plugins.bamboo.configuration.Const.API_ENV; -import static com.overops.plugins.bamboo.configuration.Const.API_TOKEN; -import static com.overops.plugins.bamboo.configuration.Const.API_URL; - -public class SettingsProcessor implements PreJobAction { - - PluginSettingsFactory pluginSettingsFactory; - - public SettingsProcessor(PluginSettingsFactory pluginSettingsFactory) { - this.pluginSettingsFactory = pluginSettingsFactory; - } - - @Override - public void execute(@NotNull final StageExecution stageExecution, @NotNull final BuildContext buildContext) { - PluginSettings pluginSettings = this.pluginSettingsFactory.createGlobalSettings(); - String url = (String) pluginSettings.get(API_URL); - String env = (String) pluginSettings.get(API_ENV); - String token = (String) pluginSettings.get(API_TOKEN); - buildContext.getBuildDefinition().getTaskDefinitions().get(0).getPluginKey(); - List tds = buildContext.getBuildDefinition().getTaskDefinitions(); - for (TaskDefinition d : tds) { - if (d.getPluginKey().equals(Const.PLUGIN_KEY)) { - Map conf = d.getConfiguration(); - conf.put(API_URL, url); - conf.put(API_ENV, env); - conf.put(API_TOKEN, token); - } - } - } -} diff --git a/src/main/java/com/overops/plugins/bamboo/TaskType.java b/src/main/java/com/overops/plugins/bamboo/TaskType.java index e29d8c8..a77021b 100644 --- a/src/main/java/com/overops/plugins/bamboo/TaskType.java +++ b/src/main/java/com/overops/plugins/bamboo/TaskType.java @@ -1,58 +1,150 @@ package com.overops.plugins.bamboo; +import java.io.PrintWriter; +import java.io.PrintStream; +import java.io.StringWriter; +import java.util.HashMap; +import java.util.Map; + + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; import com.atlassian.bamboo.build.logger.BuildLogger; +import com.atlassian.bamboo.configuration.ConfigurationMap; import com.atlassian.bamboo.process.ProcessService; import com.atlassian.bamboo.task.TaskContext; import com.atlassian.bamboo.task.TaskException; import com.atlassian.bamboo.task.TaskResult; import com.atlassian.bamboo.task.TaskResultBuilder; -import com.overops.plugins.bamboo.model.OverOpsReportModel; -import com.overops.plugins.bamboo.model.QueryOverOps; -import com.overops.plugins.bamboo.service.OverOpsService; -import com.overops.plugins.bamboo.service.impl.OverOpsServiceImpl; -import com.overops.plugins.bamboo.service.impl.ReportBuilder; -import com.overops.plugins.bamboo.utils.ReportUtils; +import com.atlassian.plugin.spring.scanner.annotation.component.Scanned; +import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport; +import com.atlassian.sal.api.pluginsettings.PluginSettings; +import com.atlassian.sal.api.pluginsettings.PluginSettingsFactory; +import com.overops.plugins.bamboo.configuration.Const; + import com.fasterxml.jackson.databind.ObjectMapper; -import org.jetbrains.annotations.NotNull; -import org.springframework.stereotype.Component; -import java.io.IOException; +import com.overops.plugins.bamboo.configuration.Const; +import com.overops.plugins.bamboo.service.impl.BambooPrintWriter; +import com.overops.report.service.QualityReportParams; +import com.overops.report.service.ReportService; +import com.overops.report.service.ReportService.Requestor; +import com.overops.report.service.model.QualityReport; +import com.overops.report.service.model.QualityReport.ReportStatus; -@Component +import org.jetbrains.annotations.NotNull; + +@Scanned public class TaskType implements com.atlassian.bamboo.task.TaskType { - private ProcessService processService; - private OverOpsService overOpsService; + @ComponentImport + private PluginSettingsFactory pluginSettingsFactory; + + private ReportService overOpsService; private ObjectMapper objectMapper; - public TaskType(final ProcessService processService) { - this.processService = processService; - this.overOpsService = new OverOpsServiceImpl(); + public TaskType(PluginSettingsFactory pluginSettingsFactory) { + this.overOpsService = new ReportService(); this.objectMapper = new ObjectMapper(); + this.pluginSettingsFactory = pluginSettingsFactory; } @NotNull @Override public TaskResult execute(@NotNull TaskContext context) throws TaskException { - TaskResultBuilder resultBuilder = TaskResultBuilder.newBuilder(context); + final BuildLogger logger = context.getBuildLogger(); - logger.addBuildLogEntry("Executing OverOps task..."); - logger.addBuildLogEntry("OverOpsBamboo plugin v." + Utils.getVersion()); - QueryOverOps query = QueryOverOps.mapToObject(context.getConfigurationMap()); + + logger.addBuildLogEntry("Generating OverOps Quality Report"); + + PluginSettings globalSettings = pluginSettingsFactory.createGlobalSettings(); + + String endPoint = (String)globalSettings.get(Const.GLOBAL_API_URL); + String apiKey = (String)globalSettings.get(Const.GLOBAL_API_TOKEN); + + QualityReportParams query = getQualityReportParams(context.getConfigurationMap()); + String envId = query.getServiceId(); + if (StringUtils.isBlank(envId)) { + query.setServiceId((String)globalSettings.get(Const.GLOBAL_ENV_ID)); + } + + TaskResultBuilder resultBuilder = TaskResultBuilder.newBuilder(context); + try { - ReportBuilder.QualityReport report = overOpsService.perform(query, logger); - OverOpsReportModel overOpsReport = ReportUtils.copyResult(report); - context.getBuildContext().getBuildResult().getCustomBuildData().put("overOpsReport", objectMapper.writeValueAsString(overOpsReport)); + logger.addBuildLogEntry("[" + Utils.getArtifactId() + " v" + Utils.getVersion() + "]"); + + boolean showAllEvents = Boolean.parseBoolean(context.getConfigurationMap().get(Const.SHOW_ALL_EVENTS)); + boolean isDebug = Boolean.parseBoolean(context.getConfigurationMap().get(Const.DEBUG)); + PrintStream printStream = isDebug ? new BambooPrintWriter(System.out, logger) : null; + + QualityReport reportModel = overOpsService.runQualityReport(endPoint, apiKey, query, Requestor.BAMBOO, printStream, isDebug); + + context.getBuildContext().getBuildResult().getCustomBuildData().put("overOpsReport", objectMapper.writeValueAsString(reportModel.getHtmlParts(showAllEvents))); context.getBuildContext().getBuildResult().getCustomBuildData().put("isOverOpsStep", "true"); - if (overOpsReport.isMarkedUnstable() && overOpsReport.isUnstable()) { + + if (reportModel.getStatusCode() == ReportStatus.FAILED) { + if ((reportModel.getExceptionDetails() != null) && Boolean.parseBoolean(context.getConfigurationMap().get(Const.PASS_BUILD_ON_QR_EXCEPTION))) { + return resultBuilder.success().build(); + } return resultBuilder.failed().build(); } else { return resultBuilder.success().build(); } + } catch (Exception e) { - logger.addErrorLogEntry(e.getMessage()); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + e.printStackTrace(pw); + + // stack trace as a string + logger.addErrorLogEntry(sw.toString()); + return resultBuilder.failed().build(); } } + private QualityReportParams getQualityReportParams(ConfigurationMap params) { + QualityReportParams qrp = new QualityReportParams(); + + qrp.setServiceId((String)params.get(Const.ENV_ID)); + qrp.setApplicationName(params.get(Const.APP_NAME)); + qrp.setDeploymentName(params.get(Const.DEP_NAME)); + + qrp.setRegexFilter(params.get(Const.REGEX_FILTER)); + qrp.setMarkUnstable(Boolean.parseBoolean(params.get(Const.MARK_UNSTABLE))); + qrp.setPrintTopIssues(NumberUtils.toInt(params.get(Const.TOP_ERROR_COUNT), 0)); + + qrp.setNewEvents(Boolean.parseBoolean(params.get(Const.CHECK_NEW_ERRORS))); + qrp.setResurfacedErrors(Boolean.parseBoolean(params.get(Const.CHECK_RESURFACED_ERRORS))); + + if (Boolean.parseBoolean(params.get(Const.CHECK_VOLUME_ERRORS))) { + qrp.setMaxErrorVolume(Math.max(1, NumberUtils.toInt(params.get(Const.MAX_ERROR_VOLUME), 1))); + } else { + qrp.setMaxErrorVolume(0); + } + + if (Boolean.parseBoolean(params.get(Const.CHECK_UNIQUE_ERRORS))) { + qrp.setMaxUniqueErrors(Math.max(1, NumberUtils.toInt(params.get(Const.MAX_UNIQUE_ERRORS), 1))); + } else { + qrp.setMaxUniqueErrors(0); + } + + if (Boolean.parseBoolean(params.get(Const.CHECK_CRITICAL_ERRORS))) { + qrp.setCriticalExceptionTypes(params.getOrDefault(Const.CRITICAL_EXCEPTION_TYPES, "")); + } else { + qrp.setCriticalExceptionTypes(""); + } + + qrp.setActiveTimespan("0"); + qrp.setBaselineTimespan("0"); + qrp.setMinVolumeThreshold(0); + qrp.setMinErrorRateThreshold(0); + qrp.setRegressionDelta(0); + qrp.setCriticalRegressionDelta(0); + qrp.setApplySeasonality(false); + + qrp.setDebug(Boolean.parseBoolean(params.get(Const.DEBUG))); + return qrp; + } } diff --git a/src/main/java/com/overops/plugins/bamboo/Utils.java b/src/main/java/com/overops/plugins/bamboo/Utils.java index d6d25da..ca55dbb 100644 --- a/src/main/java/com/overops/plugins/bamboo/Utils.java +++ b/src/main/java/com/overops/plugins/bamboo/Utils.java @@ -8,14 +8,17 @@ public class Utils { private Utils() { } + public static String getVersion() throws IOException { + return getProperty("version"); + } + + public static String getArtifactId() throws IOException { + return getProperty("artifactId"); + } - public static String getVersion() { + private static String getProperty(String prop) throws IOException { Properties props = new Properties(); - try { - props.load(Utils.class.getResourceAsStream("/version.properties")); - } catch (IOException ex) { - props.setProperty("version", "N/A"); - } - return props.getProperty("version"); + props.load(Utils.class.getResourceAsStream("/version.properties")); + return props.getProperty(prop); } } \ No newline at end of file diff --git a/src/main/java/com/overops/plugins/bamboo/configuration/ConfigTask.java b/src/main/java/com/overops/plugins/bamboo/configuration/ConfigTask.java deleted file mode 100644 index c6ebedf..0000000 --- a/src/main/java/com/overops/plugins/bamboo/configuration/ConfigTask.java +++ /dev/null @@ -1,146 +0,0 @@ -package com.overops.plugins.bamboo.configuration; - -import com.atlassian.bamboo.collections.ActionParametersMap; -import com.atlassian.bamboo.task.AbstractTaskConfigurator; -import com.atlassian.bamboo.task.BuildTaskRequirementSupport; -import com.atlassian.bamboo.task.TaskDefinition; -import com.atlassian.sal.api.pluginsettings.PluginSettings; -import com.atlassian.sal.api.pluginsettings.PluginSettingsFactory; -import org.springframework.util.StringUtils; - -import java.util.*; - -public class ConfigTask extends AbstractTaskConfigurator implements BuildTaskRequirementSupport { - PluginSettingsFactory pluginSettingsFactory; - - public ConfigTask(PluginSettingsFactory pluginSettingsFactory) { - this.pluginSettingsFactory = pluginSettingsFactory; - } - - /** - * first create - */ - public void populateContextForCreate(Map context) { - super.populateContextForCreate(context); - - PluginSettings pluginSettings = this.pluginSettingsFactory.createGlobalSettings(); - String env = (String) pluginSettings.get(Const.API_ENV); - String url = (String) pluginSettings.get(Const.API_URL); - String token = (String) pluginSettings.get(Const.API_TOKEN); - context.put(Const.API_URL, url); - context.put(Const.API_TOKEN, token); - context.put(Const.SERVICE_ID, env); - - context.put(Const.APP_NAME, Const.DEFAULT_JOB_NAME); - context.put(Const.DEP_NAME, Const.DEFAULT_DEPLOYMENT_NAME); - - context.put(Const.FIELD_CHECK_NEW_ERROR, Const.DEFAULT_CHECK_NEW_ERROR); - context.put(Const.FIELD_CHECK_RESURFACED_ERRORS, Const.DEFAULT_CHECK_RESURFACED_ERRORS); - context.put(Const.FIELD_VOLUME_ERRORS, Const.DEFAULT_VOLUME_ERRORS); - context.put(Const.FIELD_UNIQUE_ERRORS, Const.DEFAULT_UNIQUE_ERRORS); - context.put(Const.FIELD_CRITICAL_ERRORS, Const.DEFAULT_CRITICAL_ERRORS); - context.put(Const.FIELD_REGRESSIONS_ERROR, Const.DEFAULT_REGRESSIONS_ERROR); - - context.put(Const.MARK_UNSTABLE, Const.DEFAULT_MARK_UNSTABLE); - context.put(Const.TOP_ISSUE, Const.DEFAULT_PRINT_TOP_ISSUE); - context.put(Const.NEW_EVENTS, Const.DEFAULT_NEW_EVENTS); - context.put(Const.SURFACED_ERROR, Const.DEFAULT_RESURFACED_ERRORS); - context.put(Const.NEW_ERROR_VOLUME, Const.DEFAULT_MAX_ERROR_VOLUME); - context.put(Const.MAX_UNIQUE_ERROR, Const.DEFAULT_MAX_UNIQUE_ERRORS); - context.put(Const.MIN_VOLUME_THRESHOLD, Const.DEFAULT_MIN_VOLUME_THRESHOLD); - context.put(Const.MIN_ERROR_RATE_THRESHOLD, Const.DEFAULT_MIN_ERROR_RATE_THRESHOLD); - context.put(Const.REGRESSION_DELTA, Const.DEFAULT_REGRESSION_DELTA); - context.put(Const.CRITICAL_REGRESSION_DELTA, Const.DEFAULT_CRITICAL_REGRESSION_DELTA); - context.put(Const.APPLY_SEASONALITY, Const.DEFAULT_APPLY_SEASONALITY); - context.put(Const.DEBUG, Const.DEFAULT_DEBUG); - } - - /** - * from backend to view - */ - public void populateContextForEdit(Map context, TaskDefinition taskDefinition) { - super.populateContextForEdit(context, taskDefinition); - PluginSettings pluginSettings = this.pluginSettingsFactory.createGlobalSettings(); - String env = (String) pluginSettings.get(Const.API_ENV); - String url = (String) pluginSettings.get(Const.API_URL); - String token = (String) pluginSettings.get(Const.API_TOKEN); - Map config = taskDefinition.getConfiguration(); - - context.put(Const.API_URL, Optional.ofNullable(config.get(Const.API_URL)).filter(StringUtils::hasText).orElse(url)); - context.put(Const.API_TOKEN, Optional.ofNullable(config.get(Const.API_TOKEN)).filter(StringUtils::hasText).orElse(token)); - - context.put(Const.APP_NAME, Optional.ofNullable(config.get(Const.APP_NAME)).filter(StringUtils::hasText).orElse(Const.DEFAULT_JOB_NAME)); - context.put(Const.DEP_NAME, Optional.ofNullable(config.get(Const.DEP_NAME)).filter(StringUtils::hasText).orElse(Const.DEFAULT_DEPLOYMENT_NAME)); - - context.put(Const.SERVICE_ID, Optional.ofNullable(config.get(Const.SERVICE_ID)).filter(StringUtils::hasText).orElse(env)); - context.put(Const.REGEX_FILTER, config.get(Const.REGEX_FILTER)); - context.put(Const.MARK_UNSTABLE, config.get(Const.MARK_UNSTABLE)); - context.put(Const.TOP_ISSUE, config.get(Const.TOP_ISSUE)); - context.put(Const.NEW_EVENTS, config.get(Const.NEW_EVENTS)); - context.put(Const.SURFACED_ERROR, config.get(Const.SURFACED_ERROR)); - context.put(Const.NEW_ERROR_VOLUME, config.get(Const.NEW_ERROR_VOLUME)); - context.put(Const.MAX_UNIQUE_ERROR, config.get(Const.MAX_UNIQUE_ERROR)); - context.put(Const.CRITICAL_EXCEPTION_TYPE, config.get(Const.CRITICAL_EXCEPTION_TYPE)); - context.put(Const.ACTIVE_TIMESPAN, config.get(Const.ACTIVE_TIMESPAN)); - context.put(Const.BASELINE_TIMESPAN, config.get(Const.BASELINE_TIMESPAN)); - context.put(Const.MIN_VOLUME_THRESHOLD, config.get(Const.MIN_VOLUME_THRESHOLD)); - context.put(Const.MIN_ERROR_RATE_THRESHOLD, config.get(Const.MIN_ERROR_RATE_THRESHOLD)); - context.put(Const.REGRESSION_DELTA, config.get(Const.REGRESSION_DELTA)); - context.put(Const.CRITICAL_REGRESSION_DELTA, config.get(Const.CRITICAL_REGRESSION_DELTA)); - context.put(Const.APPLY_SEASONALITY, config.get(Const.APPLY_SEASONALITY)); - context.put(Const.DEBUG, config.get(Const.DEBUG)); - - context.put(Const.FIELD_CHECK_NEW_ERROR, Optional.ofNullable(config.get(Const.FIELD_CHECK_NEW_ERROR)).filter(StringUtils::hasText).orElse(null)); - context.put(Const.FIELD_CHECK_RESURFACED_ERRORS, Optional.ofNullable(config.get(Const.FIELD_CHECK_RESURFACED_ERRORS)).filter(StringUtils::hasText).orElse(null)); - context.put(Const.FIELD_VOLUME_ERRORS, Optional.ofNullable(config.get(Const.FIELD_VOLUME_ERRORS)).filter(StringUtils::hasText).orElse(null)); - context.put(Const.FIELD_UNIQUE_ERRORS, Optional.ofNullable(config.get(Const.FIELD_UNIQUE_ERRORS)).filter(StringUtils::hasText).orElse(null)); - context.put(Const.FIELD_CRITICAL_ERRORS, Optional.ofNullable(config.get(Const.FIELD_CRITICAL_ERRORS)).filter(StringUtils::hasText).orElse(null)); - context.put(Const.FIELD_REGRESSIONS_ERROR, Optional.ofNullable(config.get(Const.FIELD_REGRESSIONS_ERROR)).filter(StringUtils::hasText).orElse(null)); - } - -// -// /** -// * from gui to backend -// */ - @Override - public Map generateTaskConfigMap(ActionParametersMap params, TaskDefinition previousTaskDefinition) { - PluginSettings pluginSettings = this.pluginSettingsFactory.createGlobalSettings(); - final Map config = super.generateTaskConfigMap(params, previousTaskDefinition); - - config.put(Const.API_URL, Optional.ofNullable(params.getString(Const.API_URL)).filter(StringUtils::hasText).orElse((String) pluginSettings.get(Const.API_URL))); - config.put(Const.API_TOKEN, Optional.ofNullable(params.getString(Const.API_TOKEN)).filter(StringUtils::hasText).orElse((String) pluginSettings.get(Const.API_TOKEN))); - - config.put(Const.APP_NAME, Optional.ofNullable(params.getString(Const.APP_NAME)).filter(StringUtils::hasText).orElse(Const.DEFAULT_JOB_NAME)); - config.put(Const.DEP_NAME, Optional.ofNullable(params.getString(Const.DEP_NAME)).filter(StringUtils::hasText).orElse(Const.DEFAULT_DEPLOYMENT_NAME)); - config.put(Const.SERVICE_ID, Optional.ofNullable(params.getString(Const.SERVICE_ID)).filter(StringUtils::hasText).orElse(null)); - config.put(Const.REGEX_FILTER, Optional.ofNullable(params.getString(Const.REGEX_FILTER)).filter(StringUtils::hasText).orElse(null)); - config.put(Const.MARK_UNSTABLE, Optional.ofNullable(params.getString(Const.MARK_UNSTABLE)).filter(StringUtils::hasText).orElse(null)); - config.put(Const.TOP_ISSUE, Optional.ofNullable(params.getString(Const.TOP_ISSUE)).filter(StringUtils::hasText).orElse(null)); - config.put(Const.NEW_EVENTS, Optional.ofNullable(params.getString(Const.NEW_EVENTS)).filter(StringUtils::hasText).orElse(null)); - config.put(Const.SURFACED_ERROR, Optional.ofNullable(params.getString(Const.SURFACED_ERROR)).filter(StringUtils::hasText).orElse(null)); - config.put(Const.NEW_ERROR_VOLUME, Optional.ofNullable(params.getString(Const.NEW_ERROR_VOLUME)).filter(StringUtils::hasText).orElse(null)); - config.put(Const.MAX_UNIQUE_ERROR, Optional.ofNullable(params.getString(Const.MAX_UNIQUE_ERROR)).filter(StringUtils::hasText).orElse(null)); - config.put(Const.CRITICAL_EXCEPTION_TYPE, Optional.ofNullable(params.getString(Const.CRITICAL_EXCEPTION_TYPE)).filter(StringUtils::hasText).orElse(null)); - config.put(Const.ACTIVE_TIMESPAN, Optional.ofNullable(params.getString(Const.ACTIVE_TIMESPAN)).filter(StringUtils::hasText).orElse(null)); - config.put(Const.BASELINE_TIMESPAN, Optional.ofNullable(params.getString(Const.BASELINE_TIMESPAN)).filter(StringUtils::hasText).orElse(null)); - config.put(Const.MIN_VOLUME_THRESHOLD, Optional.ofNullable(params.getString(Const.MIN_VOLUME_THRESHOLD)).filter(StringUtils::hasText).orElse(null)); - config.put(Const.MIN_ERROR_RATE_THRESHOLD, Optional.ofNullable(params.getString(Const.MIN_ERROR_RATE_THRESHOLD)).filter(StringUtils::hasText).orElse(null)); - config.put(Const.REGRESSION_DELTA, Optional.ofNullable(params.getString(Const.REGRESSION_DELTA)).filter(StringUtils::hasText).orElse(null)); - config.put(Const.CRITICAL_REGRESSION_DELTA, Optional.ofNullable(params.getString(Const.CRITICAL_REGRESSION_DELTA)).filter(StringUtils::hasText).orElse(null)); - config.put(Const.APPLY_SEASONALITY, Optional.ofNullable(params.getString(Const.APPLY_SEASONALITY)).filter(StringUtils::hasText).orElse(null)); - config.put(Const.DEBUG, Optional.ofNullable(params.getString(Const.DEBUG)).filter(StringUtils::hasText).orElse(null)); - - - config.put(Const.FIELD_CHECK_NEW_ERROR, Optional.ofNullable(params.getString(Const.FIELD_CHECK_NEW_ERROR)).filter(StringUtils::hasText).orElse(null)); - config.put(Const.FIELD_CHECK_RESURFACED_ERRORS, Optional.ofNullable(params.getString(Const.FIELD_CHECK_RESURFACED_ERRORS)).filter(StringUtils::hasText).orElse(null)); - config.put(Const.FIELD_VOLUME_ERRORS, Optional.ofNullable(params.getString(Const.FIELD_VOLUME_ERRORS)).filter(StringUtils::hasText).orElse(null)); - config.put(Const.FIELD_UNIQUE_ERRORS, Optional.ofNullable(params.getString(Const.FIELD_UNIQUE_ERRORS)).filter(StringUtils::hasText).orElse(null)); - config.put(Const.FIELD_CRITICAL_ERRORS, Optional.ofNullable(params.getString(Const.FIELD_CRITICAL_ERRORS)).filter(StringUtils::hasText).orElse(null)); - config.put(Const.FIELD_REGRESSIONS_ERROR, Optional.ofNullable(params.getString(Const.FIELD_REGRESSIONS_ERROR)).filter(StringUtils::hasText).orElse(null)); - - - config.put(Const.API_ENV, - (String) pluginSettings.get(Const.API_ENV)); - return config; - } -} diff --git a/src/main/java/com/overops/plugins/bamboo/configuration/Const.java b/src/main/java/com/overops/plugins/bamboo/configuration/Const.java index 66c85f6..1912137 100644 --- a/src/main/java/com/overops/plugins/bamboo/configuration/Const.java +++ b/src/main/java/com/overops/plugins/bamboo/configuration/Const.java @@ -1,65 +1,47 @@ package com.overops.plugins.bamboo.configuration; public class Const { - public static final String PLUGIN_KEY="com.overops.plugins.bamboo.OverOps:overops"; - public static final String ERROR = "error"; - public static final String INFO = "info"; - public static final String API_URL = "url"; - public static final String API_ENV = "env"; - public static final String API_TOKEN = "token"; - public static final String DEFAULT_URL = "https://api.overops.com"; - public static final String URL = "url"; - public static final String ENV_ID = "evnId"; - public static final String TOKEN = "token"; + /** + * keys for PluginSettingsFactory.createGlobalSettings() + * plugins are advised to namespace the key with something unique to the plugin + * (for example "com.example.plugin:key-I-would-like-to-use" ) to avoid clashes with other keys + */ + + // PluginSettingsFactory global settings + public static final String GLOBAL_ENV_ID = "com.overops.plugins.bamboo:global-api-env-id"; + public static final String GLOBAL_API_URL = "com.overops.plugins.bamboo:global-api-url"; + public static final String GLOBAL_API_TOKEN = "com.overops.plugins.bamboo:global-api-token"; + + + // config template vars + public static final String ENV_ID = "envId"; public static final String APP_NAME = "applicationName"; public static final String DEP_NAME = "deploymentName"; - public static final String SERVICE_ID = "serviceId"; public static final String REGEX_FILTER = "regexFilter"; + public static final String TOP_ERROR_COUNT = "topErrorCount"; public static final String MARK_UNSTABLE = "markUnstable"; - public static final String TOP_ISSUE = "printTopIssues"; - public static final String NEW_EVENTS = "newEvents"; - public static final String SURFACED_ERROR = "resurfacedErrors"; - public static final String NEW_ERROR_VOLUME = "maxErrorVolume"; - public static final String MAX_UNIQUE_ERROR = "maxUniqueErrors"; - public static final String CRITICAL_EXCEPTION_TYPE = "criticalExceptionTypes"; - public static final String ACTIVE_TIMESPAN = "activeTimespan"; - public static final String BASELINE_TIMESPAN = "baselineTimespan"; - public static final String MIN_VOLUME_THRESHOLD = "minVolumeThreshold"; - public static final String MIN_ERROR_RATE_THRESHOLD = "minErrorRateThreshold"; - public static final String REGRESSION_DELTA = "regressionDelta"; - public static final String CRITICAL_REGRESSION_DELTA = "criticalRegressionDelta"; - public static final String APPLY_SEASONALITY = "applySeasonality"; - public static final String DEBUG = "debug"; + public static final String SHOW_ALL_EVENTS = "showAllEvents"; + public static final String PASS_BUILD_ON_QR_EXCEPTION = "passOnQRException"; - public static final String FIELD_CHECK_NEW_ERROR = "newEventsGate"; - public static final String FIELD_CHECK_RESURFACED_ERRORS = "checkResurfacedErrors"; - public static final String FIELD_VOLUME_ERRORS = "checkVolumeErrors"; - public static final String FIELD_UNIQUE_ERRORS = "checkUniqueErrors"; - public static final String FIELD_CRITICAL_ERRORS = "checkCriticalErrors"; - public static final String FIELD_REGRESSIONS_ERROR = "checkRegressionErrors"; + public static final String CHECK_NEW_ERRORS = "checkNewErrors"; + public static final String CHECK_RESURFACED_ERRORS = "checkResurfacedErrors"; - public static final String DEFAULT_CHECK_NEW_ERROR = "true"; - public static final String DEFAULT_CHECK_RESURFACED_ERRORS = "true"; - public static final String DEFAULT_VOLUME_ERRORS = "true"; - public static final String DEFAULT_UNIQUE_ERRORS = "true"; - public static final String DEFAULT_CRITICAL_ERRORS = "true"; - public static final String DEFAULT_REGRESSIONS_ERROR = "true"; - public static final String DEFAULT_MARK_UNSTABLE = "false"; - public static final String DEFAULT_PRINT_TOP_ISSUE = "5"; - public static final String DEFAULT_NEW_EVENTS = "false"; - public static final String DEFAULT_RESURFACED_ERRORS = "false"; - public static final String DEFAULT_MAX_ERROR_VOLUME = "0"; - public static final String DEFAULT_MAX_UNIQUE_ERRORS = "0"; - public static final String DEFAULT_MIN_VOLUME_THRESHOLD = "0"; - public static final String DEFAULT_MIN_ERROR_RATE_THRESHOLD = "0"; - public static final String DEFAULT_REGRESSION_DELTA = "0"; - public static final String DEFAULT_CRITICAL_REGRESSION_DELTA = "0"; - public static final String DEFAULT_APPLY_SEASONALITY = "false"; - public static final String DEFAULT_DEBUG = "false"; + public static final String CHECK_VOLUME_ERRORS = "checkVolumeErrors"; + public static final String MAX_ERROR_VOLUME = "maxErrorVolume"; + + public static final String CHECK_UNIQUE_ERRORS = "checkUniqueErrors"; + public static final String MAX_UNIQUE_ERRORS = "maxUniqueErrors"; + + public static final String CHECK_CRITICAL_ERRORS = "checkCriticalErrors"; + public static final String CRITICAL_EXCEPTION_TYPES = "criticalExceptionTypes"; + + public static final String DEBUG = "debug"; - public static final String DEFAULT_JOB_NAME = "${bamboo.shortJobName}"; - public static final String DEFAULT_DEPLOYMENT_NAME = "${bamboo.shortJobName}-${bamboo.buildNumber}"; + // default values + public static final String DEFAULT_API_URL = "https://api.overops.com/"; -} \ No newline at end of file + public static final String DEFAULT_TOP_ERROR_COUNT = "10"; + public static final String DEFAULT_MARK_UNSTABLE = "true"; +} diff --git a/src/main/java/com/overops/plugins/bamboo/configuration/TaskConfiguration.java b/src/main/java/com/overops/plugins/bamboo/configuration/TaskConfiguration.java new file mode 100644 index 0000000..2d6e04c --- /dev/null +++ b/src/main/java/com/overops/plugins/bamboo/configuration/TaskConfiguration.java @@ -0,0 +1,209 @@ +package com.overops.plugins.bamboo.configuration; + +import java.util.Map; +import java.util.function.Consumer; + +import com.atlassian.bamboo.collections.ActionParametersMap; +import com.atlassian.bamboo.task.AbstractTaskConfigurator; +import com.atlassian.bamboo.task.BuildTaskRequirementSupport; +import com.atlassian.bamboo.task.TaskDefinition; +import com.atlassian.bamboo.utils.error.ErrorCollection; + +import org.apache.commons.lang3.StringUtils; + + +public class TaskConfiguration extends AbstractTaskConfigurator implements BuildTaskRequirementSupport { + + private ActionParametersMap params; + private ErrorCollection errorCollection; + + /** + * first create + */ + public void populateContextForCreate(Map context) { + super.populateContextForCreate(context); + + // set minimal defaults + context.put(Const.TOP_ERROR_COUNT, Const.DEFAULT_TOP_ERROR_COUNT); + context.put(Const.MARK_UNSTABLE, Const.DEFAULT_MARK_UNSTABLE); + } + + /** + * from backend to view + */ + public void populateContextForEdit(Map context, TaskDefinition taskDefinition) { + + super.populateContextForEdit(context, taskDefinition); + + Map config = taskDefinition.getConfiguration(); + + // general settings + context.put(Const.ENV_ID, config.get(Const.ENV_ID)); + context.put(Const.APP_NAME, config.get(Const.APP_NAME)); + context.put(Const.DEP_NAME, config.get(Const.DEP_NAME)); + context.put(Const.REGEX_FILTER, config.get(Const.REGEX_FILTER)); + context.put(Const.TOP_ERROR_COUNT, config.get(Const.TOP_ERROR_COUNT)); + context.put(Const.MARK_UNSTABLE, config.get(Const.MARK_UNSTABLE)); + context.put(Const.SHOW_ALL_EVENTS, config.get(Const.SHOW_ALL_EVENTS)); + context.put(Const.PASS_BUILD_ON_QR_EXCEPTION, config.get(Const.PASS_BUILD_ON_QR_EXCEPTION)); + + // new and resurfaced quality gates + context.put(Const.CHECK_NEW_ERRORS, config.get(Const.CHECK_NEW_ERRORS)); + context.put(Const.CHECK_RESURFACED_ERRORS, config.get(Const.CHECK_RESURFACED_ERRORS)); + + // total errors quality gate + context.put(Const.CHECK_VOLUME_ERRORS, config.get(Const.CHECK_VOLUME_ERRORS)); + context.put(Const.MAX_ERROR_VOLUME, config.get(Const.MAX_ERROR_VOLUME)); + + // unique errors quality gate + context.put(Const.CHECK_UNIQUE_ERRORS, config.get(Const.CHECK_UNIQUE_ERRORS)); + context.put(Const.MAX_UNIQUE_ERRORS, config.get(Const.MAX_UNIQUE_ERRORS)); + + // critical errors quality gate + context.put(Const.CHECK_CRITICAL_ERRORS, config.get(Const.CHECK_CRITICAL_ERRORS)); + context.put(Const.CRITICAL_EXCEPTION_TYPES, config.get(Const.CRITICAL_EXCEPTION_TYPES)); + + // advanced settings + context.put(Const.DEBUG, config.get(Const.DEBUG)); + } + + /** + * from gui to backend + */ + @Override + public Map generateTaskConfigMap(ActionParametersMap params, TaskDefinition previousTaskDefinition) { + + final Map config = super.generateTaskConfigMap(params, previousTaskDefinition); + + // general settings + config.put(Const.ENV_ID, params.getString(Const.ENV_ID).toUpperCase()); // s -> S + config.put(Const.APP_NAME, params.getString(Const.APP_NAME)); + config.put(Const.DEP_NAME, params.getString(Const.DEP_NAME)); + config.put(Const.REGEX_FILTER, params.getString(Const.REGEX_FILTER)); + config.put(Const.TOP_ERROR_COUNT, params.getString(Const.TOP_ERROR_COUNT)); + config.put(Const.MARK_UNSTABLE, params.getString(Const.MARK_UNSTABLE)); + config.put(Const.SHOW_ALL_EVENTS, params.getString(Const.SHOW_ALL_EVENTS)); + config.put(Const.PASS_BUILD_ON_QR_EXCEPTION, params.getString(Const.PASS_BUILD_ON_QR_EXCEPTION)); + + // new and resurfaced quality gates + config.put(Const.CHECK_NEW_ERRORS, params.getString(Const.CHECK_NEW_ERRORS)); + config.put(Const.CHECK_RESURFACED_ERRORS, params.getString(Const.CHECK_RESURFACED_ERRORS)); + + // total errors quality gate + config.put(Const.CHECK_VOLUME_ERRORS, params.getString(Const.CHECK_VOLUME_ERRORS)); + config.put(Const.MAX_ERROR_VOLUME, params.getString(Const.MAX_ERROR_VOLUME)); + + // unique errors quality gate + config.put(Const.CHECK_UNIQUE_ERRORS, params.getString(Const.CHECK_UNIQUE_ERRORS)); + config.put(Const.MAX_UNIQUE_ERRORS, params.getString(Const.MAX_UNIQUE_ERRORS)); + + // critical errors quality gate + config.put(Const.CHECK_CRITICAL_ERRORS, params.getString(Const.CHECK_CRITICAL_ERRORS)); + config.put(Const.CRITICAL_EXCEPTION_TYPES, params.getString(Const.CRITICAL_EXCEPTION_TYPES)); + + // advanced settings + config.put(Const.DEBUG, params.getString(Const.DEBUG)); + + return config; + } + + @Override + public void validate(ActionParametersMap params, ErrorCollection errorCollection) { + + this.params = params; + this.errorCollection = errorCollection; + + String envId = params.getString(Const.ENV_ID).toUpperCase(); // s -> S + + // valid env id: must be a number starting with "S"; blank is ok + if (!StringUtils.isBlank(envId) && !envId.matches("[S]\\d+")) { + errorCollection.addError(Const.ENV_ID, "Invalid environment ID"); + } + + // validate total errors + validNumber(Const.TOP_ERROR_COUNT); + validNumber(Const.MAX_ERROR_VOLUME, Const.CHECK_VOLUME_ERRORS); + validNumber(Const.MAX_UNIQUE_ERRORS, Const.CHECK_UNIQUE_ERRORS); + + // validate critical errors + validString(Const.CRITICAL_EXCEPTION_TYPES, Const.CHECK_CRITICAL_ERRORS); + + } + + // must be a number + private void validNumber(String key) { + String value = this.params.getString(key); + try { + Integer.parseInt(value); + } catch (NumberFormatException e) { + this.errorCollection.addError(key, "Enter a number"); + } + } + + // must be a number if related quality gate is enabled + private void validNumber(String key, String conditionalKey) { + conditional(key, conditionalKey, validNumber); + } + + // must not be blank string if related quality gate is enabled + private void validString(String key, String conditionalKey) { + conditional(key, conditionalKey, validString); + } + + // must be 0 or a number followed by d, m, or h if related quality gate is enabled + private void validTimeWindow(String key, String conditionalKey) { + conditional(key, conditionalKey, validTimeWindow); + } + + // must be a number contained in the range [0,1] + private void validRange(String key, String conditionalKey) { + conditional(key, conditionalKey, validRange); + } + + // only validate a field if the quality gate is enabled + private void conditional(String key, String conditionalKey, Consumer validFunction) { + + // checkbox states are "true" and null + if (!StringUtils.isBlank(this.params.getString(conditionalKey))) { + this.params.replace(conditionalKey, "true"); // re-check the conditional's checkbox (creates logged warnings) + validFunction.accept(key); + } + } + + // reuse validateNumber() in conditionals + private Consumer validNumber = key -> validNumber(key); + + // string must not be blank + private Consumer validString = key -> { + String value = this.params.getString(key); + + if (StringUtils.isBlank(value)) { + this.errorCollection.addError(key, "Enter critical exception types"); + } + }; + + // must be a number followed by d, h or m, or be 0 + private Consumer validTimeWindow = key -> { + String value = this.params.getString(key).toLowerCase(); + + if (StringUtils.isBlank(value) || !value.matches("\\d+[dhm]")) { + this.errorCollection.addError(key, "Enter time window in days, hours, or minutes"); + } + }; + + // must be a number contained in the range [0,1] + private Consumer validRange = key -> { + String value = this.params.getString(key); + double floor = 0d; + double ceiling = 1d; + + try { + double number = Double.parseDouble(value); + if (number < floor || number > ceiling) { + throw new NumberFormatException(); + } + } catch (NumberFormatException e) { + this.errorCollection.addError(key, "Enter a number between " + floor + " and " + ceiling); + } + }; +} diff --git a/src/main/java/com/overops/plugins/bamboo/model/OOReportRegressedEvent.java b/src/main/java/com/overops/plugins/bamboo/model/OOReportRegressedEvent.java deleted file mode 100644 index baec6d8..0000000 --- a/src/main/java/com/overops/plugins/bamboo/model/OOReportRegressedEvent.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.overops.plugins.bamboo.model; - -import com.takipi.api.client.result.event.EventResult; -import com.takipi.api.client.util.cicd.OOReportEvent; -import com.takipi.api.client.util.regression.RegressionStringUtil; - -public class OOReportRegressedEvent extends OOReportEvent{ - - private final long baselineHits; - private final long baselineInvocations; - - public OOReportRegressedEvent(EventResult activeEvent, long baselineHits, long baselineInvocations, String type, String arcLink) { - - super(activeEvent, type, arcLink); - - this.baselineHits = baselineHits; - this.baselineInvocations = baselineInvocations; - } - - @Override - public String getEventRate() { - return RegressionStringUtil.getRegressedEventRate(getEvent(), baselineHits, baselineInvocations); - } - - public long getBaselineHits() { - return baselineHits; - } - - public long getBaselineCalls() { - return baselineInvocations; - } -} diff --git a/src/main/java/com/overops/plugins/bamboo/model/OverOpsReportModel.java b/src/main/java/com/overops/plugins/bamboo/model/OverOpsReportModel.java deleted file mode 100644 index 59f1a5f..0000000 --- a/src/main/java/com/overops/plugins/bamboo/model/OverOpsReportModel.java +++ /dev/null @@ -1,284 +0,0 @@ -package com.overops.plugins.bamboo.model; - -import java.util.List; - -public class OverOpsReportModel { - private boolean unstable; - private boolean markedUnstable; - private boolean passedNewErrorGate; - private boolean checkNewEvents; - private boolean passedResurfacedErrorGate; - private boolean checkResurfacedEvents; - private boolean checkCriticalErrors; - private boolean passedCriticalErrorGate; - private boolean checkTotalErrors; - private boolean passedTotalErrorGate; - private boolean checkUniqueErrors; - private boolean hasTopErrors; - private boolean passedUniqueErrorGate; - private boolean checkRegressedErrors; - private boolean passedRegressedEvents; - - private String summary; - private String newErrorSummary; - private String resurfacedErrorSummary; - private String criticalErrorSummary; - private String totalErrorSummary; - private String uniqueErrorSummary; - private String regressionSumarry; - - private List newEvents; - private List resurfacedEvents; - private List criticalEvents; - private List topEvents; - private List regressedEvents; - - public boolean isUnstable() { - return unstable; - } - - public void setUnstable(boolean unstable) { - this.unstable = unstable; - } - - public boolean isMarkedUnstable() { - return markedUnstable; - } - - public void setMarkedUnstable(boolean markedUnstable) { - this.markedUnstable = markedUnstable; - } - - public boolean isPassedNewErrorGate() { - return passedNewErrorGate; - } - - public void setPassedNewErrorGate(boolean passedNewErrorGate) { - this.passedNewErrorGate = passedNewErrorGate; - } - - public boolean isCheckNewEvents() { - return checkNewEvents; - } - - public void setCheckNewEvents(boolean checkNewEvents) { - this.checkNewEvents = checkNewEvents; - } - - public boolean isPassedResurfacedErrorGate() { - return passedResurfacedErrorGate; - } - - public void setPassedResurfacedErrorGate(boolean passedResurfacedErrorGate) { - this.passedResurfacedErrorGate = passedResurfacedErrorGate; - } - - public boolean isCheckResurfacedEvents() { - return checkResurfacedEvents; - } - - public void setCheckResurfacedEvents(boolean checkResurfacedEvents) { - this.checkResurfacedEvents = checkResurfacedEvents; - } - - public boolean isCheckCriticalErrors() { - return checkCriticalErrors; - } - - public void setCheckCriticalErrors(boolean checkCriticalErrors) { - this.checkCriticalErrors = checkCriticalErrors; - } - - public boolean isPassedCriticalErrorGate() { - return passedCriticalErrorGate; - } - - public void setPassedCriticalErrorGate(boolean passedCriticalErrorGate) { - this.passedCriticalErrorGate = passedCriticalErrorGate; - } - - public boolean isCheckTotalErrors() { - return checkTotalErrors; - } - - public void setCheckTotalErrors(boolean checkTotalErrors) { - this.checkTotalErrors = checkTotalErrors; - } - - public boolean isPassedTotalErrorGate() { - return passedTotalErrorGate; - } - - public void setPassedTotalErrorGate(boolean passedTotalErrorGate) { - this.passedTotalErrorGate = passedTotalErrorGate; - } - - public boolean isCheckUniqueErrors() { - return checkUniqueErrors; - } - - public void setCheckUniqueErrors(boolean checkUniqueErrors) { - this.checkUniqueErrors = checkUniqueErrors; - } - - public boolean isHasTopErrors() { - return hasTopErrors; - } - - public void setHasTopErrors(boolean hasTopErrors) { - this.hasTopErrors = hasTopErrors; - } - - public boolean isPassedUniqueErrorGate() { - return passedUniqueErrorGate; - } - - public void setPassedUniqueErrorGate(boolean passedUniqueErrorGate) { - this.passedUniqueErrorGate = passedUniqueErrorGate; - } - - public boolean isCheckRegressedErrors() { - return checkRegressedErrors; - } - - public void setCheckRegressedErrors(boolean checkRegressedErrors) { - this.checkRegressedErrors = checkRegressedErrors; - } - - public boolean isPassedRegressedEvents() { - return passedRegressedEvents; - } - - public void setPassedRegressedEvents(boolean passedRegressedEvents) { - this.passedRegressedEvents = passedRegressedEvents; - } - - public String getSummary() { - return summary; - } - - public void setSummary(String summary) { - this.summary = summary; - } - - public String getNewErrorSummary() { - return newErrorSummary; - } - - public void setNewErrorSummary(String newErrorSummary) { - this.newErrorSummary = newErrorSummary; - } - - public String getTotalErrorSummary() { - return totalErrorSummary; - } - - public void setTotalErrorSummary(String totalErrorSummary) { - this.totalErrorSummary = totalErrorSummary; - } - - public List getNewEvents() { - return newEvents; - } - - public void setNewEvents(List newEvents) { - this.newEvents = newEvents; - } - - public String getResurfacedErrorSummary() { - return resurfacedErrorSummary; - } - - public void setResurfacedErrorSummary(String resurfacedErrorSummary) { - this.resurfacedErrorSummary = resurfacedErrorSummary; - } - - public String getUniqueErrorSummary() { - return uniqueErrorSummary; - } - - public void setUniqueErrorSummary(String uniqueErrorSummary) { - this.uniqueErrorSummary = uniqueErrorSummary; - } - - public String getRegressionSumarry() { - return regressionSumarry; - } - - public void setRegressionSumarry(String regressionSumarry) { - this.regressionSumarry = regressionSumarry; - } - - public List getResurfacedEvents() { - return resurfacedEvents; - } - - public void setResurfacedEvents(List resurfacedEvents) { - this.resurfacedEvents = resurfacedEvents; - } - - public String getCriticalErrorSummary() { - return criticalErrorSummary; - } - - public void setCriticalErrorSummary(String criticalErrorSummary) { - this.criticalErrorSummary = criticalErrorSummary; - } - - public List getCriticalEvents() { - return criticalEvents; - } - - public void setCriticalEvents(List criticalEvents) { - this.criticalEvents = criticalEvents; - } - - public List getTopEvents() { - return topEvents; - } - - public void setTopEvents(List topEvents) { - this.topEvents = topEvents; - } - - public List getRegressedEvents() { - return regressedEvents; - } - - public void setRegressedEvents(List regressedEvents) { - this.regressedEvents = regressedEvents; - } - - @Override - public String toString() { - return "OverOpsReportModel{" + - "unstable=" + unstable + - ", markedUnstable=" + markedUnstable + - ", passedNewErrorGate=" + passedNewErrorGate + - ", checkNewEvents=" + checkNewEvents + - ", passedResurfacedErrorGate=" + passedResurfacedErrorGate + - ", checkResurfacedEvents=" + checkResurfacedEvents + - ", checkCriticalErrors=" + checkCriticalErrors + - ", passedCriticalErrorGate=" + passedCriticalErrorGate + - ", checkTotalErrors=" + checkTotalErrors + - ", passedTotalErrorGate=" + passedTotalErrorGate + - ", checkUniqueErrors=" + checkUniqueErrors + - ", hasTopErrors=" + hasTopErrors + - ", passedUniqueErrorGate=" + passedUniqueErrorGate + - ", checkRegressedErrors=" + checkRegressedErrors + - ", passedRegressedEvents=" + passedRegressedEvents + - ", summary='" + summary + '\'' + - ", newErrorSummary='" + newErrorSummary + '\'' + - ", resurfacedErrorSummary='" + resurfacedErrorSummary + '\'' + - ", criticalErrorSummary='" + criticalErrorSummary + '\'' + - ", totalErrorSummary='" + totalErrorSummary + '\'' + - ", uniqueErrorSummary='" + uniqueErrorSummary + '\'' + - ", regressionSumarry='" + regressionSumarry + '\'' + - ", newEvents=" + newEvents + - ", resurfacedEvents=" + resurfacedEvents + - ", criticalEvents=" + criticalEvents + - ", topEvents=" + topEvents + - ", regressedEvents=" + regressedEvents + - '}'; - } -} diff --git a/src/main/java/com/overops/plugins/bamboo/model/QueryOverOps.java b/src/main/java/com/overops/plugins/bamboo/model/QueryOverOps.java deleted file mode 100644 index fb8237b..0000000 --- a/src/main/java/com/overops/plugins/bamboo/model/QueryOverOps.java +++ /dev/null @@ -1,267 +0,0 @@ -package com.overops.plugins.bamboo.model; - -import com.atlassian.bamboo.configuration.ConfigurationMap; -import org.springframework.util.StringUtils; - -import java.util.Optional; - -import static com.overops.plugins.bamboo.configuration.Const.*; - -public class QueryOverOps { - private String overOpsURL; - private String overOpsSID; - private String overOpsAPIKey; - - private String applicationName; - private String deploymentName; - private String serviceId; - private String regexFilter; - private boolean markUnstable = false; - private Integer printTopIssues = 5; - private boolean newEvents = false; - private boolean resurfacedErrors = false; - private Integer maxErrorVolume = 0; - private Integer maxUniqueErrors = 0; - private String criticalExceptionTypes; - private String activeTimespan = "0"; - private String baselineTimespan = "0"; - private Integer minVolumeThreshold = 0; - private Double minErrorRateThreshold = 0d; - private Double regressionDelta = 0d; - private Double criticalRegressionDelta = 0d; - private boolean applySeasonality = false; - - private boolean debug = false; - - public String getOverOpsURL() { - return overOpsURL; - } - - public void setOverOpsURL(String overOpsURL) { - this.overOpsURL = overOpsURL; - } - - public String getOverOpsSID() { - return overOpsSID; - } - - public void setOverOpsSID(String overOpsSID) { - this.overOpsSID = overOpsSID; - } - - public String getOverOpsAPIKey() { - return overOpsAPIKey; - } - - public void setOverOpsAPIKey(String overOpsAPIKey) { - this.overOpsAPIKey = overOpsAPIKey; - } - - public String getApplicationName() { - return applicationName; - } - - public void setApplicationName(String applicationName) { - this.applicationName = applicationName; - } - - public String getDeploymentName() { - return deploymentName; - } - - public void setDeploymentName(String deploymentName) { - this.deploymentName = deploymentName; - } - - public String getServiceId() { - return serviceId; - } - - public void setServiceId(String serviceId) { - this.serviceId = serviceId; - } - - public String getRegexFilter() { - return regexFilter; - } - - public void setRegexFilter(String regexFilter) { - this.regexFilter = regexFilter; - } - - public boolean isMarkUnstable() { - return markUnstable; - } - - public void setMarkUnstable(boolean markUnstable) { - this.markUnstable = markUnstable; - } - - public Integer getPrintTopIssues() { - return printTopIssues; - } - - public void setPrintTopIssues(Integer printTopIssues) { - this.printTopIssues = printTopIssues; - } - - public boolean isNewEvents() { - return newEvents; - } - - public void setNewEvents(boolean newEvents) { - this.newEvents = newEvents; - } - - public boolean isResurfacedErrors() { - return resurfacedErrors; - } - - public void setResurfacedErrors(boolean resurfacedErrors) { - this.resurfacedErrors = resurfacedErrors; - } - - public Integer getMaxErrorVolume() { - return maxErrorVolume; - } - - public void setMaxErrorVolume(Integer maxErrorVolume) { - this.maxErrorVolume = maxErrorVolume; - } - - public Integer getMaxUniqueErrors() { - return maxUniqueErrors; - } - - public void setMaxUniqueErrors(Integer maxUniqueErrors) { - this.maxUniqueErrors = maxUniqueErrors; - } - - public String getCriticalExceptionTypes() { - return criticalExceptionTypes; - } - - public void setCriticalExceptionTypes(String criticalExceptionTypes) { - this.criticalExceptionTypes = criticalExceptionTypes; - } - - public String getActiveTimespan() { - return activeTimespan; - } - - public void setActiveTimespan(String activeTimespan) { - this.activeTimespan = activeTimespan; - } - - public String getBaselineTimespan() { - return baselineTimespan; - } - - public void setBaselineTimespan(String baselineTimespan) { - this.baselineTimespan = baselineTimespan; - } - - public Integer getMinVolumeThreshold() { - return minVolumeThreshold; - } - - public void setMinVolumeThreshold(Integer minVolumeThreshold) { - this.minVolumeThreshold = minVolumeThreshold; - } - - public Double getMinErrorRateThreshold() { - return minErrorRateThreshold; - } - - public void setMinErrorRateThreshold(Double minErrorRateThreshold) { - this.minErrorRateThreshold = minErrorRateThreshold; - } - - public Double getRegressionDelta() { - return regressionDelta; - } - - public void setRegressionDelta(Double regressionDelta) { - this.regressionDelta = regressionDelta; - } - - public Double getCriticalRegressionDelta() { - return criticalRegressionDelta; - } - - public void setCriticalRegressionDelta(Double criticalRegressionDelta) { - this.criticalRegressionDelta = criticalRegressionDelta; - } - - public boolean isApplySeasonality() { - return applySeasonality; - } - - public void setApplySeasonality(boolean applySeasonality) { - this.applySeasonality = applySeasonality; - } - - public boolean isDebug() { - return debug; - } - - public void setDebug(boolean debug) { - this.debug = debug; - } - - public static QueryOverOps mapToObject(ConfigurationMap params) { - QueryOverOps queryOverOps = new QueryOverOps(); - queryOverOps.overOpsURL = params.get(API_URL); - queryOverOps.overOpsSID = params.get(API_ENV); - queryOverOps.overOpsAPIKey = params.get(API_TOKEN); - queryOverOps.applicationName = params.get(APP_NAME); - queryOverOps.deploymentName = params.get(DEP_NAME); - - queryOverOps.serviceId = Optional.ofNullable(params.get(SERVICE_ID)).filter(StringUtils::hasText).orElse(queryOverOps.overOpsSID); - queryOverOps.regexFilter = Optional.ofNullable(params.get(REGEX_FILTER)).filter(StringUtils::hasText).orElse(""); - queryOverOps.markUnstable = Boolean.parseBoolean(Optional.ofNullable(params.get(MARK_UNSTABLE)).filter(StringUtils::hasText).orElse("false")); - queryOverOps.printTopIssues = Integer.parseInt(Optional.ofNullable(params.get(TOP_ISSUE)).filter(StringUtils::hasText).orElse("5")); - queryOverOps.newEvents = Boolean.parseBoolean(Optional.ofNullable(params.get(NEW_EVENTS)).filter(StringUtils::hasText).orElse("false")); - queryOverOps.resurfacedErrors = Boolean.parseBoolean(Optional.ofNullable(params.get(SURFACED_ERROR)).filter(StringUtils::hasText).orElse("false")); - queryOverOps.maxErrorVolume = Integer.parseInt(Optional.ofNullable(params.get(NEW_ERROR_VOLUME)).filter(StringUtils::hasText).orElse("0")); - queryOverOps.maxUniqueErrors = Integer.parseInt(Optional.ofNullable(params.get(MAX_UNIQUE_ERROR)).filter(StringUtils::hasText).orElse("0")); - queryOverOps.criticalExceptionTypes = params.getOrDefault(CRITICAL_EXCEPTION_TYPE, ""); - queryOverOps.activeTimespan = Optional.ofNullable(params.get(ACTIVE_TIMESPAN)).filter(StringUtils::hasText).orElse("0"); - queryOverOps.baselineTimespan = Optional.ofNullable(params.get(BASELINE_TIMESPAN)).filter(StringUtils::hasText).orElse("0"); - queryOverOps.minVolumeThreshold = Integer.parseInt(Optional.ofNullable(params.get(MIN_VOLUME_THRESHOLD)).filter(StringUtils::hasText).orElse("0")); - queryOverOps.minErrorRateThreshold = Double.parseDouble(Optional.ofNullable(params.get(MIN_ERROR_RATE_THRESHOLD)).filter(StringUtils::hasText).orElse("0")); - queryOverOps.regressionDelta = Double.parseDouble(Optional.ofNullable(params.get(REGRESSION_DELTA)).filter(StringUtils::hasText).orElse("0")); - queryOverOps.criticalRegressionDelta = Double.parseDouble(Optional.ofNullable(params.get(CRITICAL_REGRESSION_DELTA)).filter(StringUtils::hasText).orElse("0")); - queryOverOps.applySeasonality = Boolean.parseBoolean(Optional.ofNullable(params.get(APPLY_SEASONALITY)).filter(StringUtils::hasText).orElse("false")); - queryOverOps.debug = Boolean.parseBoolean(Optional.ofNullable(params.get(DEBUG)).filter(StringUtils::hasText).orElse("false")); - return queryOverOps; - } - - @Override - public String toString() { - return "QueryOverOps{" + - "overOpsURL='" + overOpsURL + '\'' + - ", overOpsSID='" + overOpsSID + '\'' + - ", overOpsAPIKey='" + overOpsAPIKey + '\'' + - ", applicationName='" + applicationName + '\'' + - ", deploymentName='" + deploymentName + '\'' + - ", serviceId='" + serviceId + '\'' + - ", regexFilter='" + regexFilter + '\'' + - ", markUnstable=" + markUnstable + - ", printTopIssues=" + printTopIssues + - ", newEvents=" + newEvents + - ", resurfacedErrors=" + resurfacedErrors + - ", maxErrorVolume=" + maxErrorVolume + - ", maxUniqueErrors=" + maxUniqueErrors + - ", criticalExceptionTypes='" + criticalExceptionTypes + '\'' + - ", activeTimespan='" + activeTimespan + '\'' + - ", baselineTimespan='" + baselineTimespan + '\'' + - ", minVolumeThreshold=" + minVolumeThreshold + - ", minErrorRateThreshold=" + minErrorRateThreshold + - ", regressionDelta=" + regressionDelta + - ", criticalRegressionDelta=" + criticalRegressionDelta + - ", applySeasonality=" + applySeasonality + - ", debug=" + debug + - '}'; - } -} diff --git a/src/main/java/com/overops/plugins/bamboo/model/ReportEventModel.java b/src/main/java/com/overops/plugins/bamboo/model/ReportEventModel.java deleted file mode 100644 index 3e7d9a2..0000000 --- a/src/main/java/com/overops/plugins/bamboo/model/ReportEventModel.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.overops.plugins.bamboo.model; - -public class ReportEventModel { - private String arcLink; - private String type; - private String applications; - private String introducedBy; - private String eventSummary; - private String eventRate; - private long hits; - private long calls; - - public ReportEventModel() { - } - - public ReportEventModel(String arcLink, String type, String introducedBy) { - this.arcLink = arcLink; - this.type = type; - this.introducedBy = introducedBy; - } - - public ReportEventModel(String arcLink, String type, String introducedBy, String eventSummary, String eventRate, long hits, long calls, String applications) { - this.arcLink = arcLink; - this.type = type; - this.introducedBy = introducedBy; - this.eventSummary = eventSummary; - this.eventRate = eventRate; - this.hits = hits; - this.calls = calls; - this.applications = applications; - } - - public String getArcLink() { - return arcLink; - } - - public void setArcLink(String arcLink) { - this.arcLink = arcLink; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getApplications() { - return applications; - } - - public void setApplications(String applications) { - this.applications = applications; - } - - public String getIntroducedBy() { - return introducedBy; - } - - public void setIntroducedBy(String introducedBy) { - this.introducedBy = introducedBy; - } - - public String getEventSummary() { - return eventSummary; - } - - public void setEventSummary(String eventSummary) { - this.eventSummary = eventSummary; - } - - public String getEventRate() { - return eventRate; - } - - public void setEventRate(String eventRate) { - this.eventRate = eventRate; - } - - public long getHits() { - return hits; - } - - public void setHits(long hits) { - this.hits = hits; - } - - public long getCalls() { - return calls; - } - - public void setCalls(long calls) { - this.calls = calls; - } -} diff --git a/src/main/java/com/overops/plugins/bamboo/model/ReportRegressedModel.java b/src/main/java/com/overops/plugins/bamboo/model/ReportRegressedModel.java deleted file mode 100644 index bde8fa6..0000000 --- a/src/main/java/com/overops/plugins/bamboo/model/ReportRegressedModel.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.overops.plugins.bamboo.model; - -public class ReportRegressedModel extends ReportEventModel { - private long baselineHits; - private long baselineInvocations; - private String eventRate; - - public long getBaselineHits() { - return baselineHits; - } - - public void setBaselineHits(long baselineHits) { - this.baselineHits = baselineHits; - } - - public long getBaselineInvocations() { - return baselineInvocations; - } - - public void setBaselineInvocations(long baselineInvocations) { - this.baselineInvocations = baselineInvocations; - } - - public String getEventRate() { - return eventRate; - } - - public void setEventRate(String eventRate) { - this.eventRate = eventRate; - } -} diff --git a/src/main/java/com/overops/plugins/bamboo/model/TestServiceResponse.java b/src/main/java/com/overops/plugins/bamboo/model/TestServiceResponse.java deleted file mode 100644 index e07455b..0000000 --- a/src/main/java/com/overops/plugins/bamboo/model/TestServiceResponse.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.overops.plugins.bamboo.model; - -public class TestServiceResponse { - private boolean status; - private String message; - - public TestServiceResponse() { - } - - public TestServiceResponse(boolean status, String message) { - this.status = status; - this.message = message; - } - - public boolean isStatus() { - return status; - } - - public void setStatus(boolean status) { - this.status = status; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - - public static TestServiceResponse ok(String message) { - return new TestServiceResponse(true, message); - } - - public static TestServiceResponse fail(String message) { - return new TestServiceResponse(false, message); - } -} diff --git a/src/main/java/com/overops/plugins/bamboo/service/OverOpsService.java b/src/main/java/com/overops/plugins/bamboo/service/OverOpsService.java deleted file mode 100644 index ace12cd..0000000 --- a/src/main/java/com/overops/plugins/bamboo/service/OverOpsService.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.overops.plugins.bamboo.service; - - - -import com.atlassian.bamboo.build.logger.BuildLogger; -import com.overops.plugins.bamboo.model.QueryOverOps; -import com.overops.plugins.bamboo.service.impl.ReportBuilder; - -import java.io.IOException; - -public interface OverOpsService { - ReportBuilder.QualityReport perform(QueryOverOps queryOverOps, BuildLogger logger) throws IOException, InterruptedException; -} diff --git a/src/main/java/com/overops/plugins/bamboo/service/SettingService.java b/src/main/java/com/overops/plugins/bamboo/service/SettingService.java deleted file mode 100644 index 35209cb..0000000 --- a/src/main/java/com/overops/plugins/bamboo/service/SettingService.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.overops.plugins.bamboo.service; - - -import com.overops.plugins.bamboo.model.TestServiceResponse; - -public interface SettingService { - TestServiceResponse testConnection(String url, String env, String token); -} diff --git a/src/main/java/com/overops/plugins/bamboo/service/impl/OverOpsServiceImpl.java b/src/main/java/com/overops/plugins/bamboo/service/impl/OverOpsServiceImpl.java deleted file mode 100644 index 3d537e7..0000000 --- a/src/main/java/com/overops/plugins/bamboo/service/impl/OverOpsServiceImpl.java +++ /dev/null @@ -1,218 +0,0 @@ -package com.overops.plugins.bamboo.service.impl; - -import com.atlassian.bamboo.build.logger.BuildLogger; -import com.overops.plugins.bamboo.model.QueryOverOps; -import com.overops.plugins.bamboo.service.OverOpsService; -import com.takipi.api.client.RemoteApiClient; -import com.takipi.api.client.data.view.SummarizedView; -import com.takipi.api.client.observe.Observer; -import com.takipi.api.client.util.regression.RegressionInput; -import com.takipi.api.client.util.view.ViewUtil; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; - -import java.io.IOException; -import java.io.PrintStream; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Objects; -import java.util.regex.Pattern; - -@Component -public class OverOpsServiceImpl implements OverOpsService { - private static final String SEPERATOR = ","; - private boolean runRegressions = false; - - @Override - public ReportBuilder.QualityReport perform(QueryOverOps queryOverOps, BuildLogger logger) throws IOException, InterruptedException { - PrintStream printStream; - if (convertToMinutes(queryOverOps.getBaselineTimespan()) > 0) { - runRegressions = true; - } - if (queryOverOps.isDebug()) { - printStream = new BambooPrintWriter(System.out, logger); - } else { - printStream = null; - } - - pauseForTheCause(printStream); - - validateInputs(queryOverOps, printStream); - - RemoteApiClient apiClient = (RemoteApiClient) RemoteApiClient.newBuilder().setHostname(queryOverOps.getOverOpsURL()).setApiKey(queryOverOps.getOverOpsAPIKey()).build(); - - if (Objects.nonNull(printStream) && (queryOverOps.isDebug())) { - apiClient.addObserver(new ApiClientObserver(printStream, queryOverOps.isDebug())); - } - - SummarizedView allEventsView = ViewUtil.getServiceViewByName(apiClient, queryOverOps.getServiceId().toUpperCase(), "All Events"); - - if (Objects.isNull(allEventsView)) { - if(Objects.nonNull(printStream)) { - printStream.println("Could not acquire ID for 'All Events'. Please check connection to " + queryOverOps.getOverOpsURL()); - } - throw new IllegalStateException( - "Could not acquire ID for 'All Events'. Please check connection to " + queryOverOps.getOverOpsURL()); - } - - RegressionInput input = setupRegressionData(queryOverOps, allEventsView, printStream); - return ReportBuilder.execute(apiClient, input, queryOverOps.getMaxErrorVolume(), queryOverOps.getMaxUniqueErrors(), - queryOverOps.getPrintTopIssues(), queryOverOps.getRegexFilter(), queryOverOps.isNewEvents(), queryOverOps.isResurfacedErrors(), - runRegressions, queryOverOps.isMarkUnstable(), printStream, queryOverOps.isDebug()); - - } - - //sleep for 30 seconds to ensure all data is in OverOps - private static void pauseForTheCause(PrintStream printStream) { - if (Objects.nonNull(printStream)) { - printStream.println("Build Step: Starting OverOps Quality Gate...."); - } - try { - Thread.sleep(30000); - } catch (Exception e) { - if (Objects.nonNull(printStream)) { - printStream.println("Can not hold the process."); - } - } - } - - private void validateInputs (QueryOverOps queryOverOps, PrintStream printStream) { - String apiHost = queryOverOps.getOverOpsURL(); - String apiKey = queryOverOps.getOverOpsAPIKey(); - - if (StringUtils.isEmpty(apiHost)) { - throw new IllegalArgumentException("Missing host name"); - } - - if (StringUtils.isEmpty(apiKey)) { - throw new IllegalArgumentException("Missing api key"); - } - - //validate active and baseline time window - if (!"0".equalsIgnoreCase(queryOverOps.getActiveTimespan())) { - if (convertToMinutes(queryOverOps.getActiveTimespan()) == 0) { - printStream.println("For Increasing Error Gate, the active timewindow currently set to: " + queryOverOps.getActiveTimespan() + " is not properly formated. See help for format instructions."); - throw new IllegalArgumentException("For Increasing Error Gate, the active timewindow currently set to: " + queryOverOps.getActiveTimespan() + " is not properly formated. See help for format instructions."); - } - } - if (!"0".equalsIgnoreCase(queryOverOps.getBaselineTimespan())) { - if (convertToMinutes(queryOverOps.getBaselineTimespan()) == 0) { - printStream.println("For Increasing Error Gate, the baseline timewindow currently set to: " + queryOverOps.getBaselineTimespan() + " cannot be zero or is improperly formated. See help for format instructions."); - throw new IllegalArgumentException("For Increasing Error Gate, the baseline timewindow currently set to: " + queryOverOps.getBaselineTimespan() + " cannot be zero or is improperly formated. See help for format instructions."); - } - } - - - if (StringUtils.isEmpty(queryOverOps.getOverOpsSID())) { - throw new IllegalArgumentException("Missing environment Id"); - } - - } - - private RegressionInput setupRegressionData(QueryOverOps queryOverOps, SummarizedView allEventsView, PrintStream printStream) - throws InterruptedException, IOException { - - RegressionInput input = new RegressionInput(); - input.serviceId = queryOverOps.getServiceId(); - input.viewId = allEventsView.id; - input.applictations = parseArrayString(queryOverOps.getApplicationName(), printStream, "Application Name"); - input.deployments = parseArrayString(queryOverOps.getDeploymentName(), printStream, "Deployment Name"); - input.criticalExceptionTypes = parseArrayString(queryOverOps.getCriticalExceptionTypes(), printStream, - "Critical Exception Types"); - - if (runRegressions) { - input.activeTimespan = convertToMinutes(queryOverOps.getActiveTimespan()); - input.baselineTime = queryOverOps.getBaselineTimespan(); - input.baselineTimespan = convertToMinutes(queryOverOps.getBaselineTimespan()); - input.minVolumeThreshold = queryOverOps.getMinVolumeThreshold(); - input.minErrorRateThreshold = queryOverOps.getMinErrorRateThreshold(); - input.regressionDelta = queryOverOps.getRegressionDelta(); - input.criticalRegressionDelta = queryOverOps.getCriticalRegressionDelta(); - input.applySeasonality = queryOverOps.isApplySeasonality(); - input.validate(); - } - - printInputs(queryOverOps, printStream, input); - - return input; - } - - private int convertToMinutes(String timeWindow) { - - if (StringUtils.isEmpty(timeWindow)) { - return 0; - } - - if (timeWindow.toLowerCase().contains("d")) { - int days = Integer.parseInt(timeWindow.substring(0, timeWindow.indexOf("d"))); - return days * 24 * 60; - } else if (timeWindow.toLowerCase().contains("h")) { - int hours = Integer.parseInt(timeWindow.substring(0, timeWindow.indexOf("h"))); - return hours * 60; - } else if (timeWindow.toLowerCase().contains("m")) { - return Integer.parseInt(timeWindow.substring(0, timeWindow.indexOf("m"))); - } - - return 0; - } - - private static Collection parseArrayString(String value, PrintStream printStream, String name) { - if (StringUtils.isEmpty(value)) { - return Collections.emptySet(); - } - - return Arrays.asList(value.trim().split(Pattern.quote(SEPERATOR))); - } - - private void printInputs(QueryOverOps queryOverOps, PrintStream printStream, RegressionInput input) { - - if (Objects.nonNull(printStream)) { - printStream.println(input); - - printStream.println("Max unique errors = " + queryOverOps.getMaxUniqueErrors()); - printStream.println("Max error volume = " + queryOverOps.getMaxErrorVolume()); - printStream.println("Check new errors = " + queryOverOps.isNewEvents()); - printStream.println("Check resurfaced errors = " + queryOverOps.isResurfacedErrors()); - - String regexPrint; - - if (Objects.nonNull(queryOverOps.getRegexFilter())) { - regexPrint = queryOverOps.getRegexFilter(); - } else { - regexPrint = ""; - } - - printStream.println("Regex filter = " + regexPrint); - } - } - - protected static class ApiClientObserver implements Observer { - - private final PrintStream printStream; - private final boolean verbose; - - public ApiClientObserver(PrintStream printStream, boolean verbose) { - this.printStream = printStream; - this.verbose = verbose; - } - - @Override - public void observe(Operation operation, String url, String request, String response, int responseCode, long time) { - StringBuilder output = new StringBuilder(); - - output.append(String.valueOf(operation)); - output.append(" took "); - output.append(time / 1000); - output.append("ms for "); - output.append(url); - - if (verbose) { - output.append(". Response: "); - output.append(response); - } - - printStream.println(output.toString()); - } - } -} diff --git a/src/main/java/com/overops/plugins/bamboo/service/impl/ReportBuilder.java b/src/main/java/com/overops/plugins/bamboo/service/impl/ReportBuilder.java deleted file mode 100644 index 802bff7..0000000 --- a/src/main/java/com/overops/plugins/bamboo/service/impl/ReportBuilder.java +++ /dev/null @@ -1,452 +0,0 @@ -package com.overops.plugins.bamboo.service.impl; - -import com.overops.plugins.bamboo.model.OOReportRegressedEvent; -import com.google.gson.Gson; -import com.takipi.api.client.ApiClient; -import com.takipi.api.client.result.event.EventResult; -import com.takipi.api.client.util.cicd.OOReportEvent; -import com.takipi.api.client.util.cicd.ProcessQualityGates; -import com.takipi.api.client.util.cicd.QualityGateReport; -import com.takipi.api.client.util.regression.*; - -import java.io.PrintStream; -import java.util.*; -import java.util.regex.Pattern; - -public class ReportBuilder { - - public static class QualityReport { - - private final List newIssues; - private final List regressions; - private final List criticalErrors; - private final List topErrors; - private final List resurfacedErrors; - private final List allIssues; - private final boolean unstable; - private final RegressionInput input; - private final RateRegression regression; - private final long eventVolume; - private final int uniqueEventsCount; - private final boolean checkNewGate; - private final boolean checkResurfacedGate; - private final boolean checkCriticalGate; - private final boolean checkVolumeGate; - private final boolean checkUniqueGate; - private final boolean checkRegressionGate; - private final Integer maxEventVolume; - private final Integer maxUniqueVolume; - private final boolean markedUnstable; - - protected QualityReport(RegressionInput input, RateRegression regression, - List regressions, List criticalErrors, - List topErrors, List newIssues, - List resurfacedErrors, long eventVolume, int uniqueEventCounts, boolean unstable, - boolean checkNewGate, boolean checkResurfacedGate, boolean checkCriticalGate, boolean checkVolumeGate, - boolean checkUniqueGate, boolean checkRegressionGate, Integer maxEventVolume, Integer maxUniqueVolume, boolean markedUnstable) { - - this.input = input; - this.regression = regression; - - this.regressions = regressions; - this.allIssues = new ArrayList(); - this.newIssues = newIssues; - this.criticalErrors = criticalErrors; - this.topErrors = topErrors; - this.resurfacedErrors = resurfacedErrors; - - if (regressions != null) { - allIssues.addAll(regressions); - } - - this.eventVolume = eventVolume; - this.uniqueEventsCount = uniqueEventCounts; - this.unstable = unstable; - this.checkNewGate = checkNewGate; - this.checkResurfacedGate = checkResurfacedGate; - this.checkCriticalGate = checkCriticalGate; - this.checkVolumeGate = checkVolumeGate; - this.checkUniqueGate = checkUniqueGate; - this.checkRegressionGate = checkRegressionGate; - this.maxEventVolume = maxEventVolume; - this.maxUniqueVolume = maxUniqueVolume; - this.markedUnstable = markedUnstable; - } - - public RegressionInput getInput() { - return input; - } - - public RateRegression getRegression() { - return regression; - } - - public List getResurfacedErrors() { - return resurfacedErrors; - } - - public List getAllIssues() { - return allIssues; - } - - public List getCriticalErrors() { - return criticalErrors; - } - - public List getTopErrors() { - return topErrors; - } - - public List getNewIssues() { - return newIssues; - } - - public List getRegressions() { - return regressions; - } - - public long getUniqueEventsCount() { - return uniqueEventsCount; - } - - public boolean getUnstable() { - return unstable; - } - - public long getEventVolume() { - return eventVolume; - } - - public boolean isCheckNewGate() { - return checkNewGate; - } - - public boolean isCheckResurfacedGate() { - return checkResurfacedGate; - } - - public boolean isCheckCriticalGate() { - return checkCriticalGate; - } - - public boolean isCheckVolumeGate() { - return checkVolumeGate; - } - - public boolean isCheckUniqueGate() { - return checkUniqueGate; - } - - public boolean isCheckRegressionGate() { - return checkRegressionGate; - } - - public Integer getMaxEventVolume() { - return maxEventVolume; - } - - public Integer getMaxUniqueVolume() { - return maxUniqueVolume; - } - - public boolean isMarkedUnstable() { - return markedUnstable; - } - - public String getDeploymentName() { - if (Objects.nonNull(getInput()) && Objects.nonNull(getInput().deployments)) { - String value = getInput().deployments.toString(); - value = value.replace("[", ""); - value = value.replace("]", ""); - return value; - } - return ""; - } - } - - private static class ReportVolume { - protected List topEvents; - protected Collection filter; - - } - - private static boolean allowEvent(EventResult event, Pattern pattern) { - - if (pattern == null ) { - return true; - } - - String json = new Gson().toJson(event); - boolean result = !pattern.matcher(json).find(); - - return result; - } - - private static List getSortedEventsByVolume(Collection events) { - - List result = new ArrayList(events); - - result.sort((o1, o2) -> { - long v1; - long v2; - - if (o1.stats != null) { - v1 = o1.stats.hits; - } else { - v1 = 0; - } - - if (o2.stats != null) { - v2 = o2.stats.hits; - } else { - v2 = 0; - } - - return (int)(v2 - v1); - }); - - return result; - } - - private static void addEvent(Set events, EventResult event, - Pattern pattern, PrintStream output, boolean verbose) { - - if (allowEvent(event, pattern)) { - events.add(event); - } else if ((output != null) && (verbose)) { - output.println(event + " did not match regexFilter and was skipped"); - } - } - - private static Collection filterEvents(RateRegression rateRegression, - Pattern pattern, PrintStream output, boolean verbose) { - - Set result = new HashSet<>(); - - if (pattern != null) { - - for (EventResult event : rateRegression.getNonRegressions()) { - addEvent(result, event, pattern, output, verbose); - } - - for (EventResult event : rateRegression.getAllNewEvents().values()) { - addEvent(result, event, pattern, output, verbose); - } - - for (RegressionResult regressionResult : rateRegression.getAllRegressions().values()) { - addEvent(result, regressionResult.getEvent(), pattern, output, verbose); - - } - - } else { - result.addAll(rateRegression.getNonRegressions()); - result.addAll(rateRegression.getAllNewEvents().values()); - - for (RegressionResult regressionResult : rateRegression.getAllRegressions().values()) { - result.add(regressionResult.getEvent()); - } - } - - return result; - } - - private static ReportVolume getReportVolume(ApiClient apiClient, - RegressionInput input, RateRegression rateRegression, - int limit, String regexFilter, PrintStream output, boolean verbose) { - - ReportVolume result = new ReportVolume(); - - Pattern pattern; - - if ((regexFilter != null) && (regexFilter.length() > 0)) { - pattern = Pattern.compile(regexFilter); - } else { - pattern = null; - } - - Collection eventsSet = filterEvents(rateRegression, pattern, output, verbose); - List events = getSortedEventsByVolume(eventsSet); - - if (pattern != null) { - result.filter = eventsSet; - } - - result.topEvents = new ArrayList<>(); - - for (EventResult event : events) { - if (event.stats != null) { - if (result.topEvents.size() < limit) { - String arcLink = ProcessQualityGates.getArcLink(apiClient, event.id, input, rateRegression.getActiveWndowStart()); - result.topEvents.add(new OOReportEvent(event, arcLink)); - } - } - } - - return result; - } - - /* - * Entry point into report engine - */ - public static QualityReport execute(ApiClient apiClient, RegressionInput input, - Integer maxEventVolume, Integer maxUniqueErrors, int topEventLimit, String regexFilter, - boolean newEvents, boolean resurfacedEvents, boolean runRegressions, boolean markedUnstable, PrintStream output, boolean verbose) { - - //check if total or unique gates are being tested - boolean countGate = false; - if (maxEventVolume != 0 || maxUniqueErrors != 0) { - countGate = true; - } - boolean checkMaxEventGate = maxEventVolume != 0; - boolean checkUniqueEventGate = maxUniqueErrors != 0; - - //get the CICD quality report for all gates but Regressions - //initialize the QualityGateReport so we don't get null pointers below - QualityGateReport qualityGateReport = new QualityGateReport(); - if (countGate || newEvents || resurfacedEvents || regexFilter != null) { - qualityGateReport = ProcessQualityGates.processCICDInputs(apiClient, input, newEvents, resurfacedEvents, - regexFilter, topEventLimit, countGate, output, verbose); - } - - //run the regression gate - ReportVolume reportVolume; - RateRegression rateRegression = null; - List regressions = null; - boolean hasRegressions = false; - if (runRegressions) { - rateRegression = RegressionUtil.calculateRateRegressions(apiClient, input, output, verbose); - - reportVolume = getReportVolume(apiClient, input, - rateRegression, topEventLimit, regexFilter, output, verbose); - - regressions = getAllRegressions(apiClient, input, rateRegression, reportVolume.filter); - if (regressions != null && regressions.size() > 0) { - hasRegressions = true; - replaceSourceId2(regressions); - } - } - - //max total error gate - boolean maxVolumeExceeded = (maxEventVolume != 0) && (qualityGateReport.getTotalErrorCount() > maxEventVolume); - - //max unique error gate - long uniqueEventCount; - boolean maxUniqueErrorsExceeded; - if (maxUniqueErrors != 0) { - uniqueEventCount = qualityGateReport.getUniqueErrorCount(); - maxUniqueErrorsExceeded = uniqueEventCount > maxUniqueErrors; - } else { - uniqueEventCount = 0; - maxUniqueErrorsExceeded = false; - } - - //new error gate - boolean newErrors = false; - if (qualityGateReport.getNewErrors() != null && qualityGateReport.getNewErrors().size() > 0) { - newErrors = true; - replaceSourceId(qualityGateReport.getNewErrors()); - } - - //resurfaced error gate - boolean resurfaced = false; - if (qualityGateReport.getResurfacedErrors() != null && qualityGateReport.getResurfacedErrors().size() > 0) { - resurfaced = true; - replaceSourceId(qualityGateReport.getResurfacedErrors()); - } - - //critical error gate - boolean critical = false; - if (qualityGateReport.getCriticalErrors() != null && qualityGateReport.getCriticalErrors().size() > 0) { - critical = true; - replaceSourceId(qualityGateReport.getCriticalErrors()); - } - - //top errors - if (qualityGateReport.getTopErrors() != null && qualityGateReport.getTopErrors().size() > 0) { - replaceSourceId(qualityGateReport.getTopErrors()); - } - - boolean checkCritical = false; - if (input.criticalExceptionTypes != null && input.criticalExceptionTypes.size() > 0) { - checkCritical = true; - } - - boolean unstable = (hasRegressions) - || (maxVolumeExceeded) - || (maxUniqueErrorsExceeded) - || (newErrors) - || (resurfaced) - || (critical); - - return new QualityReport(input, rateRegression, regressions, - qualityGateReport.getCriticalErrors(), qualityGateReport.getTopErrors(), qualityGateReport.getNewErrors(), - qualityGateReport.getResurfacedErrors(), qualityGateReport.getTotalErrorCount(), - qualityGateReport.getUniqueErrorCount(), unstable, newEvents, resurfacedEvents, checkCritical, checkMaxEventGate, - checkUniqueEventGate, runRegressions, maxEventVolume, maxUniqueErrors, markedUnstable); - } - - //for each event, replace the source ID in the ARC link with the number 4 (which means Jenkins) - private static void replaceSourceId (List events) { - for (OOReportEvent ooReportEvent : events) { - String arcLink = replaceSourceIdInArcLink(ooReportEvent.getARCLink()); - ooReportEvent.setArcLink(arcLink); - } - } - - //for each event, replace the source ID in the ARC link with the number 4 (which means Jenkins) - private static void replaceSourceId2 (List events) { - for (OOReportEvent ooReportEvent : events) { - String arcLink = replaceSourceIdInArcLink(ooReportEvent.getARCLink()); - ooReportEvent.setArcLink(arcLink); - } - } - - private static String replaceSourceIdInArcLink(String arcLink) { - if (arcLink == null) { - return arcLink; - } - String returnString; - CharSequence target = "source=43"; - CharSequence replacement = "source=4"; - - returnString = arcLink.replace(target, replacement); - - return returnString; - } - - private static List getAllRegressions(ApiClient apiClient, - RegressionInput input, RateRegression rateRegression, Collection filter) { - - List result = new ArrayList(); - - for (RegressionResult regressionResult : rateRegression.getCriticalRegressions().values()) { - - if ((filter != null) && (!filter.contains(regressionResult.getEvent()))) { - continue; - } - - String arcLink = ProcessQualityGates.getArcLink(apiClient, regressionResult.getEvent().id, input, rateRegression.getActiveWndowStart()); - - OOReportRegressedEvent regressedEvent = new OOReportRegressedEvent(regressionResult.getEvent(), - regressionResult.getBaselineHits(), regressionResult.getBaselineInvocations(), RegressionStringUtil.SEVERE_REGRESSION, arcLink); - - result.add(regressedEvent); - } - - for (RegressionResult regressionResult : rateRegression.getAllRegressions().values()) { - - if (rateRegression.getCriticalRegressions().containsKey(regressionResult.getEvent().id)) { - continue; - } - - String arcLink = ProcessQualityGates.getArcLink(apiClient, regressionResult.getEvent().id, input, rateRegression.getActiveWndowStart()); - - OOReportRegressedEvent regressedEvent = new OOReportRegressedEvent(regressionResult.getEvent(), - regressionResult.getBaselineHits(), regressionResult.getBaselineInvocations(), RegressionStringUtil.REGRESSION, arcLink); - - result.add(regressedEvent); - } - - return result; - } -} diff --git a/src/main/java/com/overops/plugins/bamboo/service/impl/SettingServiceImpl.java b/src/main/java/com/overops/plugins/bamboo/service/impl/SettingServiceImpl.java deleted file mode 100644 index 91d2818..0000000 --- a/src/main/java/com/overops/plugins/bamboo/service/impl/SettingServiceImpl.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.overops.plugins.bamboo.service.impl; - -import com.overops.plugins.bamboo.model.TestServiceResponse; -import com.overops.plugins.bamboo.service.SettingService; -import com.takipi.api.client.ApiClient; -import com.takipi.api.client.RemoteApiClient; -import com.takipi.api.client.data.service.SummarizedService; -import com.takipi.api.client.util.client.ClientUtil; -import com.takipi.api.core.url.UrlClient; - -import java.util.List; - -public class SettingServiceImpl implements SettingService { - @Override - public TestServiceResponse testConnection(String url, String env, String token) { - try { - RemoteApiClient apiClient = (RemoteApiClient) RemoteApiClient.newBuilder().setHostname(url).setApiKey(token).build(); - - UrlClient.Response response = apiClient.testConnection(); - - boolean testConnection = (response == null) || (response.isBadResponse()); - boolean testService = ((env == null) || (hasAccessToService(apiClient, env))); - - if (testConnection) { - int code; - - if (response != null) { - code = response.responseCode; - } else { - code = -1; - } - - return TestServiceResponse.fail("Unable to connect to API server. Code: " + code); - } - - if (!testService) { - return TestServiceResponse.fail("API key has no access to environment " + env); - } - - return TestServiceResponse.ok("Connection Successful."); - } catch (Exception e) { - return TestServiceResponse.fail("REST API error : " + e.getMessage()); - } - } - - - private boolean hasAccessToService(ApiClient apiClient, String serviceId) { - - List services; - - try { - services = ClientUtil.getEnvironments(apiClient); - } catch (Exception e) { - return false; - } - - - for (SummarizedService service : services) { - if (service.id.equals(serviceId)) { - return true; - } - } - - return false; - - } -} diff --git a/src/main/java/com/overops/plugins/bamboo/servlet/AdminServlet.java b/src/main/java/com/overops/plugins/bamboo/servlet/AdminServlet.java index ab69371..7230044 100644 --- a/src/main/java/com/overops/plugins/bamboo/servlet/AdminServlet.java +++ b/src/main/java/com/overops/plugins/bamboo/servlet/AdminServlet.java @@ -1,112 +1,192 @@ package com.overops.plugins.bamboo.servlet; -import com.atlassian.sal.api.transaction.TransactionCallback; - import java.io.IOException; import java.util.HashMap; +import java.util.List; import java.util.Map; -import java.util.Optional; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import com.atlassian.templaterenderer.TemplateRenderer; +import com.atlassian.plugin.spring.scanner.annotation.component.Scanned; +import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport; import com.atlassian.sal.api.pluginsettings.PluginSettings; import com.atlassian.sal.api.pluginsettings.PluginSettingsFactory; -import com.atlassian.templaterenderer.TemplateRenderer; -import com.atlassian.sal.api.transaction.TransactionTemplate; -import com.overops.plugins.bamboo.model.TestServiceResponse; -import com.overops.plugins.bamboo.service.SettingService; -import com.overops.plugins.bamboo.service.impl.SettingServiceImpl; +import com.takipi.api.client.ApiClient; +import com.takipi.api.client.RemoteApiClient; +import com.takipi.api.client.util.client.ClientUtil; +import com.takipi.api.client.data.service.SummarizedService; + import com.overops.plugins.bamboo.configuration.Const; -import org.springframework.util.StringUtils; +import org.apache.commons.lang3.StringUtils; + +@Scanned public class AdminServlet extends HttpServlet { -public static final String OVEROPS_ADMIN_VM = "overopsadmin.vm"; - private final TransactionTemplate transactionTemplate; - private final PluginSettingsFactory pluginSettingsFactory; private static final long serialVersionUID = 1L; - private final String URL_REGEX = "^https://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]"; - private final TemplateRenderer renderer; - private SettingService settingService; + // velocity variables + private enum VM { + template("default-settings.vm"), + envId("envId"), + apiUrl("apiUrl"), + apiToken("apiToken"), + save("save"), + testConnection("testConnection"), + isError("isError"), + isSuccess("isSuccess"), + message("message"); + + private final String var; + + VM(String var) { + this.var = var; + } + + public String get() { + return var; + } + + } + @ComponentImport + private final PluginSettingsFactory pluginSettingsFactory; + + @ComponentImport + private final TemplateRenderer renderer; - public AdminServlet(PluginSettingsFactory pluginSettingsFactory, TemplateRenderer renderer, - TransactionTemplate transactionTemplate) { + public AdminServlet(PluginSettingsFactory pluginSettingsFactory, TemplateRenderer renderer) { this.pluginSettingsFactory = pluginSettingsFactory; this.renderer = renderer; - this.transactionTemplate = transactionTemplate; - this.settingService = new SettingServiceImpl(); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - Map context = new HashMap(); - resp.setContentType("text/html;charset=utf-8"); + // get/set global plugin settings PluginSettings pluginSettings = pluginSettingsFactory.createGlobalSettings(); - String url = Optional.ofNullable(pluginSettings.get(Const.API_URL)).map(u -> (String) u).filter(StringUtils::hasLength).map(String::trim) - .orElse(Const.DEFAULT_URL); - context.put(Const.API_URL, url.trim()); - String env = Optional.ofNullable(pluginSettings.get(Const.API_ENV)).map(e -> (String) e).map(String::trim).orElse(""); + // velocity template context + Map context = new HashMap(); + + // API URL: if blank, set default value + String globalApiUrl = (String) pluginSettings.get(Const.GLOBAL_API_URL); + String url = StringUtils.isBlank(globalApiUrl) ? Const.DEFAULT_API_URL : globalApiUrl; + + // ENV, TOKEN: blank is default value (replace null, whitespace with blank) + String globalEnvId = (String) pluginSettings.get(Const.GLOBAL_ENV_ID); + String env = StringUtils.isBlank(globalEnvId) ? "" : globalEnvId; + + String globalApiToken = (String) pluginSettings.get(Const.GLOBAL_API_TOKEN); + String token = StringUtils.isBlank(globalApiToken) ? "" : globalApiToken; - context.put(Const.API_ENV, env); + // set velocity context + context.put(VM.apiUrl.get(), url); + context.put(VM.envId.get(), env); + context.put(VM.apiToken.get(), token); - String token = Optional.ofNullable(pluginSettings.get(Const.API_TOKEN)).map(t -> (String) t).map(String::trim).orElse(""); - context.put(Const.API_TOKEN, token); - context.put(Const.ERROR, ""); - context.put(Const.INFO, ""); - context.put("newEventsGate", false); - renderer.render(OVEROPS_ADMIN_VM, context, resp.getWriter()); + // render page + resp.setContentType("text/html;charset=utf-8"); + renderer.render(VM.template.get(), context, resp.getWriter()); } @Override protected void doPost(final HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + + // velocity template context Map context = new HashMap(); - resp.setContentType("text/html;charset=utf-8"); - String url = req.getParameter(Const.API_URL).trim().replaceAll("/+$", ""); - String env = req.getParameter(Const.API_ENV).trim(); - String token = req.getParameter(Const.API_TOKEN).trim(); + // populate values from form + String env = req.getParameter(VM.envId.get()).trim().toUpperCase(); + String url = req.getParameter(VM.apiUrl.get()).trim(); + String token = req.getParameter(VM.apiToken.get()).trim(); - context.put(Const.API_URL, url); - context.put(Const.API_ENV, env); - context.put(Const.API_TOKEN, token); + // check if 'save' or 'test connection' was submitted + boolean isSave = StringUtils.isNotBlank(req.getParameter(VM.save.get())); + boolean isTestConnection = StringUtils.isNotBlank(req.getParameter(VM.testConnection.get())); - if (!url.matches(URL_REGEX)) { - context.put(Const.ERROR, "OverOps url is not saved! Invalid format!"); - context.put(Const.INFO, ""); + if (isSave) { - renderer.render(OVEROPS_ADMIN_VM, context, resp.getWriter()); - return; - } + // save form + PluginSettings pluginSettings = pluginSettingsFactory.createGlobalSettings(); + pluginSettings.put(Const.GLOBAL_ENV_ID, env); + pluginSettings.put(Const.GLOBAL_API_URL, url); + pluginSettings.put(Const.GLOBAL_API_TOKEN, token); + + context.put(VM.isSuccess.get(), "true"); + context.put(VM.message.get(), "Settings saved"); + + } else if (isTestConnection) { + + if (StringUtils.isBlank(url)) { + + // URL can't be blank + context.put(VM.isError.get(), "true"); + context.put(VM.message.get(), "Unable to connect. API URL cannot be blank."); + + } else if (StringUtils.isBlank(token)) { + + // API Token can't be blank + context.put(VM.isError.get(), "true"); + context.put(VM.message.get(), "Unable to connect. API Token cannot be blank."); - try { - TestServiceResponse response = settingService.testConnection(url, env, token); - - if (response.isStatus()) { - transactionTemplate.execute((TransactionCallback) () -> { - PluginSettings pluginSettings = pluginSettingsFactory.createGlobalSettings(); - pluginSettings.put(Const.API_URL, url); - pluginSettings.put(Const.API_ENV, env); - pluginSettings.put(Const.API_TOKEN, token); - return pluginSettings; - }); - context.put(Const.INFO, "OverOps settings are updated. Check that jobs are configured properly"); - context.put(Const.ERROR, ""); } else { - context.put(Const.ERROR, response.getMessage()); - context.put(Const.INFO, ""); + + // test credentials + RemoteApiClient apiClient = + (RemoteApiClient) RemoteApiClient.newBuilder() + .setHostname(url) + .setApiKey(token) + .build(); + + if (!apiClient.validateConnection()) { + // error - can't connect to host + context.put(VM.isError.get(), "true"); + context.put(VM.message.get(), "Unable to connect. Check API URL."); + } else if (!hasAccessToEnvironment(apiClient, env)) { + // error - no access to env + context.put(VM.isError.get(), "true"); + context.put(VM.message.get(), "Permission denied. Check Environment ID and API Token."); + } else { + // success + context.put(VM.isSuccess.get(), "true"); + context.put(VM.message.get(), "Connection successful. Click 'save' to save settings."); + } } + } + + // set velocity context + context.put(VM.apiUrl.get(), url); + context.put(VM.envId.get(), env); + context.put(VM.apiToken.get(), token); + + // render template + resp.setContentType("text/html;charset=utf-8"); + renderer.render(VM.template.get(), context, resp.getWriter()); + } + + private static boolean hasAccessToEnvironment(ApiClient apiClient, String envId) { + + List environments; + + try { + environments = ClientUtil.getEnvironments(apiClient); } catch (Exception e) { - context.put(Const.ERROR, "OverOps settings were not saved! Check server url, environment id or token."); - context.put(Const.INFO, ""); - } finally { - renderer.render(OVEROPS_ADMIN_VM, context, resp.getWriter()); + return false; } + + for (SummarizedService env : environments) { + if (env.id.equals(envId)) { + return true; + } + } + + return false; + } + } diff --git a/src/main/java/com/overops/plugins/bamboo/ui/ViewOverOpsReport.java b/src/main/java/com/overops/plugins/bamboo/ui/ViewOverOpsReport.java index 7604856..cc54ad5 100644 --- a/src/main/java/com/overops/plugins/bamboo/ui/ViewOverOpsReport.java +++ b/src/main/java/com/overops/plugins/bamboo/ui/ViewOverOpsReport.java @@ -6,7 +6,7 @@ import com.atlassian.bamboo.resultsummary.BuildResultsSummary; import com.atlassian.bamboo.resultsummary.BuildResultsSummaryImpl; import com.atlassian.bamboo.resultsummary.ResultsSummary; -import com.overops.plugins.bamboo.model.OverOpsReportModel; +import com.overops.report.service.model.HtmlParts; import com.overops.plugins.bamboo.utils.Util; import org.apache.log4j.Logger; @@ -21,7 +21,7 @@ public class ViewOverOpsReport extends PlanResultsAction { private static final long serialVersionUID = 1L; private static final Logger log = Logger.getLogger(ViewOverOpsReport.class); - private OverOpsReportModel report; + private HtmlParts report; public String execute() throws Exception { String result = super.execute(); @@ -37,7 +37,7 @@ public String execute() throws Exception { for (String key : customBuildData.keySet()) { if (key.startsWith("overOpsReport")) { log.debug("Found report link for master =" + key); - report = Util.stringToObject(customBuildData.get(key), OverOpsReportModel.class); + report = Util.stringToObject(customBuildData.get(key), HtmlParts.class); } } } @@ -49,7 +49,7 @@ public String execute() throws Exception { for (String key : customBuildData.keySet()) { if (key.startsWith("overOpsReport")) { log.debug("Found report link for master =" + key); - report = Util.stringToObject(customBuildData.get(key), OverOpsReportModel.class); + report = Util.stringToObject(customBuildData.get(key), HtmlParts.class); } } } @@ -57,11 +57,11 @@ public String execute() throws Exception { return result; } - public OverOpsReportModel getReport() { + public HtmlParts getReport() { return report; } - public void setReport(OverOpsReportModel report) { + public void setReport(HtmlParts report) { this.report = report; } } diff --git a/src/main/java/com/overops/plugins/bamboo/ui/ViewOverOpsReportCondition.java b/src/main/java/com/overops/plugins/bamboo/ui/ViewOverOpsReportCondition.java index f5e6a3a..bfed7de 100644 --- a/src/main/java/com/overops/plugins/bamboo/ui/ViewOverOpsReportCondition.java +++ b/src/main/java/com/overops/plugins/bamboo/ui/ViewOverOpsReportCondition.java @@ -12,6 +12,8 @@ import com.atlassian.bamboo.resultsummary.ResultsSummary; import com.atlassian.bamboo.resultsummary.ResultsSummaryManager; import com.atlassian.plugin.PluginParseException; +import com.atlassian.plugin.spring.scanner.annotation.component.Scanned; +import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport; import com.atlassian.plugin.web.Condition; import org.apache.log4j.Logger; @@ -19,10 +21,14 @@ import java.util.Map; import java.util.Set; +@Scanned public class ViewOverOpsReportCondition implements Condition { private static final Logger log = Logger.getLogger(ViewOverOpsReportCondition.class); + @ComponentImport private PlanManager planManager; + + @ComponentImport private ResultsSummaryManager resultsSummaryManager; public ViewOverOpsReportCondition(PlanManager planManager, ResultsSummaryManager resultsSummaryManager) { diff --git a/src/main/java/com/overops/plugins/bamboo/utils/ReportUtils.java b/src/main/java/com/overops/plugins/bamboo/utils/ReportUtils.java deleted file mode 100644 index d6cd997..0000000 --- a/src/main/java/com/overops/plugins/bamboo/utils/ReportUtils.java +++ /dev/null @@ -1,215 +0,0 @@ -package com.overops.plugins.bamboo.utils; - -import com.overops.plugins.bamboo.model.OverOpsReportModel; -import com.overops.plugins.bamboo.model.ReportEventModel; -import com.overops.plugins.bamboo.service.impl.ReportBuilder; -import com.takipi.api.client.util.cicd.OOReportEvent; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; - -public class ReportUtils { - - public static OverOpsReportModel copyResult(ReportBuilder.QualityReport report) { - OverOpsReportModel result = new OverOpsReportModel(); - result.setMarkedUnstable(report.isMarkedUnstable()); - result.setUnstable(report.getUnstable()); - result.setSummary(getSummary(report)); - result.setPassedNewErrorGate(getPassedNewErrorGate(report)); - result.setCheckNewEvents(getCheckNewEvents(report)); - result.setNewErrorSummary(getNewErrorSummary(report)); - result.setNewEvents(getNewEvents(report).stream().map(e -> new ReportEventModel(e.getARCLink(), e.getType(), e.getIntroducedBy(), e.getEventSummary(), e.getEventRate(), e.getHits(), e.getCalls(), e.getApplications())).collect(Collectors.toList())); - result.setPassedResurfacedErrorGate(getPassedResurfacedErrorGate(report)); - result.setCheckResurfacedEvents(getCheckResurfacedEvents(report)); - result.setResurfacedErrorSummary(getResurfacedErrorSummary(report)); - result.setResurfacedEvents(getResurfacedEvents(report).stream().map(e -> new ReportEventModel(e.getARCLink(), e.getType(), e.getIntroducedBy(), e.getEventSummary(), e.getEventRate(), e.getHits(), e.getCalls(), e.getApplications())).collect(Collectors.toList())); - result.setCheckCriticalErrors(getCheckCriticalErrors(report)); - result.setPassedCriticalErrorGate(getPassedCriticalErrorGate(report)); - result.setCriticalErrorSummary(getCriticalErrorSummary(report)); - result.setCriticalEvents(getCriticalEvents(report).stream().map(e -> new ReportEventModel(e.getARCLink(), e.getType(), e.getIntroducedBy(), e.getEventSummary(), e.getEventRate(), e.getHits(), e.getCalls(), e.getApplications())).collect(Collectors.toList())); - result.setCheckTotalErrors(getCheckTotalErrors(report)); - result.setPassedTotalErrorGate(getPassedTotalErrorGate(report)); - result.setTotalErrorSummary(getTotalErrorSummary(report)); - result.setCheckUniqueErrors(getCheckUniqueErrors(report)); - result.setHasTopErrors(getHasTopErrors(report)); - result.setPassedUniqueErrorGate(getPassedUniqueErrorGate(report)); - result.setUniqueErrorSummary(getUniqueErrorSummary(report)); - result.setTopEvents(getTopEvents(report).stream().map(e -> new ReportEventModel(e.getARCLink(), e.getType(), e.getIntroducedBy(), e.getEventSummary(), e.getEventRate(), e.getHits(), e.getCalls(), e.getApplications())).collect(Collectors.toList())); - result.setRegressionSumarry(getRegressionSumarry(report)); - result.setCheckRegressedErrors(getCheckRegressedErrors(report)); - result.setPassedRegressedEvents(getPassedRegressedEvents(report)); - result.setRegressedEvents(getRegressedEvents(report).stream().map(e -> new ReportEventModel(e.getARCLink(), e.getType(), e.getIntroducedBy(), e.getEventSummary(), e.getEventRate(), e.getHits(), e.getCalls(), e.getApplications())).collect(Collectors.toList())); - return result; - } - - private static String getDeploymentName(ReportBuilder.QualityReport report) { - return Optional.of(report.getInput()).filter(e -> Objects.nonNull(e.deployments)) - .map(e -> e.deployments).map(Object::toString).map(e -> e.replace("[", "")) - .map(e -> e.replace("]", "")).orElse(""); - } - - private static String getSummary(ReportBuilder.QualityReport report) { - if (report.getUnstable() && report.isMarkedUnstable()) { - //the build is unstable when marking the build as unstable - return "OverOps has marked build " + getDeploymentName(report) + " as unstable because the below quality gate(s) were not met."; - } else if (!report.isMarkedUnstable() && report.getUnstable()) { - //unstable build stable when NOT marking the build as unstable - return "OverOps has detected issues with build " + getDeploymentName(report) + " but did not mark the build as unstable."; - } else { - //stable build when marking the build as unstable - return "Congratulations, build " + getDeploymentName(report) + " has passed all quality gates!"; - } - } - - private static boolean getPassedNewErrorGate(ReportBuilder.QualityReport report) { - return getCheckNewEvents(report) && !getNewErrorsExist(report); - } - - private static boolean getCheckNewEvents(ReportBuilder.QualityReport report) { - return report.isCheckNewGate(); - } - - private static String getNewErrorSummary(ReportBuilder.QualityReport report) { - if (getNewEvents(report).size() > 0) { - return "New Error Gate: Failed, OverOps detected " + report.getNewIssues().size() + " new error(s) in your build."; - } else if (report.isCheckNewGate()) { - return "New Error Gate: Passed, OverOps did not detect any new errors in your build."; - } - - return null; - } - - private static boolean getNewErrorsExist(ReportBuilder.QualityReport report) { - return getNewEvents(report).size() > 0; - } - - private static List getNewEvents(ReportBuilder.QualityReport report) { - return Optional.ofNullable(report.getNewIssues()).orElse(new ArrayList<>()); - } - - private static boolean getPassedResurfacedErrorGate(ReportBuilder.QualityReport report) { - return getCheckResurfacedEvents(report) && !getResurfacedErrorsExist(report); - } - - private static boolean getResurfacedErrorsExist(ReportBuilder.QualityReport report) { - return getResurfacedEvents(report).size() > 0; - } - - private static boolean getCheckResurfacedEvents(ReportBuilder.QualityReport report) { - return report.isCheckResurfacedGate(); - } - - private static String getResurfacedErrorSummary(ReportBuilder.QualityReport report) { - if (getResurfacedEvents(report).size() > 0) { - return "Resurfaced Error Gate: Failed, OverOps detected " + report.getResurfacedErrors().size() + " resurfaced errors in your build."; - } else if (report.isCheckResurfacedGate()) { - return "Resurfaced Error Gate: Passed, OverOps did not detect any resurfaced errors in your build."; - } - - return null; - } - - private static List getResurfacedEvents(ReportBuilder.QualityReport report) { - return Optional.ofNullable(report.getResurfacedErrors()).orElse(new ArrayList<>()); - } - - private static boolean getCheckCriticalErrors(ReportBuilder.QualityReport report) { - return report.isCheckCriticalGate(); - } - - private static boolean getPassedCriticalErrorGate(ReportBuilder.QualityReport report) { - return getCheckCriticalErrors(report) && !getCriticalErrorsExist(report); - } - - private static boolean getCriticalErrorsExist(ReportBuilder.QualityReport report) { - return getCriticalEvents(report).size() > 0; - } - - private static String getCriticalErrorSummary(ReportBuilder.QualityReport report) { - if (getCriticalEvents(report).size() > 0) { - return "Critical Error Gate: Failed, OverOps detected " + report.getCriticalErrors().size() + " critical errors in your build."; - } else if (report.isCheckCriticalGate()) { - return "Critical Error Gate: Passed, OverOps did not detect any critical errors in your build."; - } - - return null; - } - - private static List getCriticalEvents(ReportBuilder.QualityReport report) { - return Optional.ofNullable(report.getCriticalErrors()).orElse(new ArrayList<>()); - } - - private static boolean getCountGates(ReportBuilder.QualityReport report) { - return getCheckUniqueErrors(report) || getCheckTotalErrors(report); - } - - private static boolean getCheckTotalErrors(ReportBuilder.QualityReport report) { - return report.isCheckVolumeGate(); - } - - private static boolean getPassedTotalErrorGate(ReportBuilder.QualityReport report) { - return getCheckTotalErrors(report) && (report.getEventVolume() > 0 && report.getEventVolume() < report.getMaxEventVolume()); - } - - private static String getTotalErrorSummary(ReportBuilder.QualityReport report) { - if (report.getEventVolume() > 0 && report.getEventVolume() >= report.getMaxEventVolume()) { - return "Total Error Volume Gate: Failed, OverOps detected " + report.getEventVolume() + " total errors which is >= the max allowable of " + report.getMaxEventVolume(); - } else if (report.getEventVolume() > 0 && report.getEventVolume() < report.getMaxEventVolume()) { - return "Total Error Volume Gate: Passed, OverOps detected " + report.getEventVolume() + " total errors which is < than max allowable of " + report.getMaxEventVolume(); - } - - return null; - } - - private static boolean getCheckUniqueErrors(ReportBuilder.QualityReport report) { - return report.isCheckUniqueGate(); - } - - private static boolean getHasTopErrors(ReportBuilder.QualityReport report) { - return !getPassedTotalErrorGate(report) || !getPassedUniqueErrorGate(report); - } - - private static boolean getPassedUniqueErrorGate(ReportBuilder.QualityReport report) { - return getCheckUniqueErrors(report) && (report.getUniqueEventsCount() > 0 && report.getUniqueEventsCount() < report.getMaxUniqueVolume()); - } - - private static String getUniqueErrorSummary(ReportBuilder.QualityReport report) { - if (report.getUniqueEventsCount() > 0 && report.getUniqueEventsCount() >= report.getMaxUniqueVolume()) { - return "Unique Error Volume Gate: Failed, OverOps detected " + report.getUniqueEventsCount() + " unique errors which is >= the max allowable of " + report.getMaxUniqueVolume(); - } else if (report.getUniqueEventsCount() > 0 && report.getUniqueEventsCount() < report.getMaxUniqueVolume()) { - return "Unique Error Volume Gate: Passed, OverOps detected " + report.getUniqueEventsCount() + " unique errors which is < than max allowable of " + report.getMaxUniqueVolume(); - } - - return null; - } - - private static List getTopEvents(ReportBuilder.QualityReport report) { - return Optional.ofNullable(report.getTopErrors()).orElse(new ArrayList<>()); - } - - private static String getRegressionSumarry(ReportBuilder.QualityReport report) { - String baselineTime = Objects.nonNull(report.getInput()) ? report.getInput().baselineTime : ""; - if (!getPassedRegressedEvents(report)) { - return "Increasing Quality Gate: Failed, OverOps detected increasing errors in the current build against the baseline of " + baselineTime; - } else if (getPassedRegressedEvents(report)) { - return "Increasing Quality Gate: Passed, OverOps did not detect any increasing errors in the current build against the baseline of " + baselineTime; - } - - return null; - } - - private static boolean getCheckRegressedErrors(ReportBuilder.QualityReport report) { - return report.isCheckRegressionGate(); - } - - private static boolean getPassedRegressedEvents(ReportBuilder.QualityReport report) { - return !(getCheckRegressedErrors(report) && report.getRegressions() != null && report.getRegressions().size() > 0); - } - - private static List getRegressedEvents(ReportBuilder.QualityReport report) { - return Optional.ofNullable(report.getAllIssues()).orElse(new ArrayList<>()); - } -} diff --git a/src/main/resources/META-INF/spring/plugin.xml b/src/main/resources/META-INF/spring/plugin.xml new file mode 100644 index 0000000..146f83c --- /dev/null +++ b/src/main/resources/META-INF/spring/plugin.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/src/main/resources/OverOps.properties b/src/main/resources/OverOps.properties deleted file mode 100644 index 6d3b97b..0000000 --- a/src/main/resources/OverOps.properties +++ /dev/null @@ -1,45 +0,0 @@ -#put any key/value pairs here -overops-administration.label=OverOps Configuration -overops-administration.name=OverOps Configuration -overops-administration.description=The OverOps Configuration Plugin - -overops.task.help.link=https://github.com/takipi/bamboo-overops-plugin -overops.task.help.title=How to configure OverOps task ? - -overops.admin.url=OverOps URL -overops.admin.evn=OverOps Env ID -overops.admin.token=OverOps API Token - -user-key-settingspage.label=Settings page -user-key-settingspage.name=Settings page -user-key-settingspage.description=The User Key Settings page Plugin - -overops.config.applicationName=Application Name -overops.config.deploymentName=Deployment Name -overops.config.serviceId=Environment ID -overops.config.regexFilter=Regex Filter -overops.config.markUnstable=Mark Build Unstable -overops.config.printTopIssues=Show Top Issues -overops.config.newEvents=Detect New Errors -overops.config.resurfacedErrors=Detect Resurfaced Errors -overops.config.maxErrorVolume=Max Allowable Error Volume -overops.config.maxUniqueErrors=Max Allowable Unique Error Count -overops.config.criticalExceptionTypes=Detect Critical Exception Types -overops.config.activeTimespan=Active Time Window -overops.config.baselineTimespan=Baseline Time Window -overops.config.minVolumeThreshold=Error Volume Threshold -overops.config.minErrorRateThreshold=Error Rate Threshold (0-1) -overops.config.regressionDelta=Regression Delta (0-1) -overops.config.criticalRegressionDelta=Critical Regression Threshold (0-1) -overops.config.applySeasonality=Apply Seasonality -overops.config.debug=Debug Mode - -overops.config.newEventsGate=New Error Gate -overops.config.checkResurfacedErrors=Resurfaced Error Gate -overops.config.checkVolumeErrors=Total Error Volume Gate -overops.config.checkUniqueErrors=Unique Error Volume Gate -overops.config.checkCriticalErrors=Critical Exception Type(s) Gate -overops.config.checkRegressionErrors=Increasing Errors Gate - -overopsQualityReport.description=Display the OverOps Report page -overopsQualityReport.label=OverOps Quality Report diff --git a/src/main/resources/atlassian-plugin.xml b/src/main/resources/atlassian-plugin.xml index 9d37578..84a0fed 100644 --- a/src/main/resources/atlassian-plugin.xml +++ b/src/main/resources/atlassian-plugin.xml @@ -1,6 +1,5 @@ - + ${project.description} ${project.version} @@ -9,88 +8,71 @@ images/pluginOverOpsLogo.png - - + + - - - com.atlassian.auiplugin:ajs - atl.general - - - - - - + + + OverOps administration servlet + /overops/admin - - - - - - - + - The OverOps Configuration Plugin + + OverOps administration settings section - - + - The User Key Settings page Plugin + + OverOps settings page - - /plugins/servlet/overops/admin + + /plugins/servlet/overops/admin - - Checks if settings are still valid for the job - - - - Execute the OverOps Quality Gates as part of your build - + + + + Generates an OverOps Quality Report for your build - - - + + - + + + OverOps Quality Report template - + /templates/viewOverOpsReport.ftl - - - Display the OverOps Report page - \ No newline at end of file +