Interlok

Adaptris


Integration anytime any place anywhere.


Interlok verify feeding sonar

Creating sonar issues based on Interlok config test.

Since starting on our journey of maintaining a build-parent we’ve been constantly improving it, and now using a private fork of it in our internal build pipeline.

One rabbit hole I’ve recently travelled (which has since made it as an improvement) was trying to feed information from interlokVerify into sonarqube.

TLDR

The end result is this:

Sonar Violation Report

To use it in a project add the following config:

sonarqube {
  properties {
    property "sonar.projectKey", "<projectKey>"
    property "sonar.organization", "<organization>"
    property "sonar.host.url", "<sonar_host>"
    property "sonar.sourceEncoding", "UTF-8"
    property "sonar.sources", "./src/main/interlok"
    property "sonar.tests", "./src/test/interlok"
    property "sonar.externalIssuesReportPaths", interlokVerifySonarReport
  }
}

There are working examples using sonarcloud.io, found at interlok-hello-world and interlok-twilio-sms.

The Journey

There are three parts to this journey:

  • Sonarqube and getting information into it
  • Interlok Verify and getting information out of it
  • Gluing the two together

Sonarqube

Sonarqube is a code quality and security monitoring tool, it provides mechanisms of performing and reporting on static code analysis, as well as taking inputs from other analysis tools.

It support the ability to provide issues in its generic issue import format. This allowed us to create our own report and use it as input to sonar using the sonar.externalIssuesReportPaths property.

Interlok Verify

One simple yet powerful step that runs as a part of the check tasks in the build parent, is interlokVerify under the covers it runs the gradle equivalent of the following:

java -jar ./lib/interlok-boot.jar -configtest

Config test attempts to unmarshal the config based on bootstrap.properties and executes the component init() and prepare() for the unmarshaled config.

An output taken from interlok-hello-world looks something like this:

Bootstrap of Interlok 3.10-SNAPSHOT(2020-05-17:04:02:24 UTC) complete
INFO  [main] [com.adaptris.core.management.UnifiedBootstrap}] Adapter created
WARN  [main] [com.adaptris.core.util.LoggingHelper}] [PayloadFromMetadataService(set-payload)] is a payload-from-metadata-service; use payload-from-template or metadata-to-payload instead.
WARN  [main] [com.adaptris.core.Adapter}] [Adapter(hello-world)] has a MessageErrorHandler with no behaviour; messages may be discarded upon exception
DEBUG [main] [com.adaptris.core.Adapter}] FailedMessageRetrier []
Config check only; terminating

A development team practise is adding logging warning during a components initialisation when its deprecated, or to configuration that may cause unexpected behavior, as seen above.

With a combination of redirecting the output from interlokVerify:

def interlokVerify = tasks.register("interlokVerify", JavaExec) {
  // ...
  systemProperty "interlok.logging.url", verifyLog4j
  standardOutput = new ByteArrayOutputStream()
  doLast {
    def verifyOutput = standardOutput.toString()
    mkdir(reportFile.getParentFile())
    reportFile.withOutputStream { stream ->
      standardOutput.writeTo(stream)
    }
  }
}

and log4j2.xml filtering, which is referred to using the interlok.logging.url system property:

<Loggers>
  <Logger name="org" level="OFF"/>
  <Logger name="io" level="OFF"/>
  <Logger name="com" level="OFF"/>
  <Logger name="net" level="OFF"/>
  <Logger name="jndi" level="OFF"/>
  <Logger name="java" level="OFF"/>
  <Logger name="com.adaptris" level="WARN"/>
  <Root level="OFF">
   <AppenderRef ref="Console"/>
  </Root>
</Loggers>

We end up with a report.txt with the warnings that config test reported:

[PayloadFromMetadataService(set-payload)] is a payload-from-metadata-service; use payload-from-template or metadata-to-payload instead.
[Adapter(hello-world)] has a MessageErrorHandler with no behaviour; messages may be discarded upon exception

Gluing the two together

After extracting the information from interlokVerify and knowing it was possible to import a “generic issue report”.

The next step was creating the report this was done by writing a java command line app: interlok-verify-report.

Which takes the report.txt and outputs a the “generic issue format”:

{
  "issues" : [ {
    "engineId" : "interlokVerify",
    "ruleId" : "rule1",
    "severity" : "INFO",
    "type" : "CODE_SMELL",
    "primaryLocation" : {
      "message" : "[PayloadFromMetadataService(set-payload)] is a payload-from-metadata-service; use payload-from-template or metadata-to-payload instead."
    }
  }]
}

Executing is done as a part of the build parent:

def interlokVerifyReport = tasks.register("interlokVerifyReport", JavaExec) {
    group = 'Test'
    description = 'Create Interlok verify test reports'
    onlyIf{
      interlokVerify.get().didWork
    }
    main = 'com.adaptris.labs.verify.CreateVerifyReport'
    classpath = configurations.interlokVerifyReport
    args "--reportFile"
    args interlokVerifyTextReport
    args "--outputFile"
    args interlokVerifySonarReport
}

The Future

At the moment all of the violations are set to INFO this is partly due to the fact we’re relying on logging output to produce the report. What we’ve now realised is the potential for further improvements of --configtest and making it more aware of annotations and outputting a report itself to move away from log manipulation. This would open up the potential for increasing the severity.