Skip to content
This repository has been archived by the owner on May 28, 2024. It is now read-only.

Commit

Permalink
Enable experimental callflow feature for jenkins plugin - SDEV-940 (#304
Browse files Browse the repository at this point in the history
)
  • Loading branch information
chriswininger authored Apr 9, 2024
1 parent a48dc2d commit fe854a6
Show file tree
Hide file tree
Showing 12 changed files with 411 additions and 90 deletions.
16 changes: 15 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@
<enforcer.skip>true</enforcer.skip> <!-- TODO numerous requireUpperBoundDeps, some probably indicative of real problems -->
<jvnet-localizer-plugin.version>1.23</jvnet-localizer-plugin.version>
<forkCount>1</forkCount>
<nexus-platform-api.version>5.0.3-01</nexus-platform-api.version>

<nexus-platform-api.version>5.0.4-SNAPSHOT</nexus-platform-api.version>

<buildsupport.version>36</buildsupport.version>
<buildsupport.license-maven-plugin.version>4.1</buildsupport.license-maven-plugin.version>
Expand Down Expand Up @@ -205,6 +206,19 @@
<version>3.4.2</version>
</dependency>

<!-- pinning the version for transitives-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.26.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.1.2-jre</version>
</dependency>


<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright (c) 2016-present Sonatype, Inc. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the Apache License Version 2.0 is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
*/

package org.sonatype.nexus.ci.iq

import hudson.Extension
import hudson.model.AbstractDescribableImpl
import hudson.model.Descriptor
import org.kohsuke.stapler.DataBoundConstructor
import org.kohsuke.stapler.DataBoundSetter

class CallflowConfiguration
extends AbstractDescribableImpl<CallflowConfiguration>
{
List<ScanPattern> callflowScanPatterns

List<String> callflowNamespaces

// This is used for experimental configuration. The options will not be widely publicised but can allow us to
// internally test different callflow options
Map<String, Object> additionalConfiguration

@DataBoundConstructor
CallflowConfiguration(
final List<ScanPattern> callflowScanPatterns = null,
final List<String> callflowNamespaces = null,
final Map<String, Object> additionalConfiguration = null
) {
this.callflowScanPatterns = callflowScanPatterns
this.callflowNamespaces = callflowNamespaces
this.additionalConfiguration = additionalConfiguration
}

List<ScanPattern> getCallflowScanPatterns() {
return callflowScanPatterns
}

@DataBoundSetter
void setCallflowScanPatterns(final List<ScanPattern> callflowScanPatterns) {
this.callflowScanPatterns = callflowScanPatterns
}

List<String> getCallflowNamespaces() {
return callflowNamespaces
}

@DataBoundSetter
void setCallflowNamespaces(final List<String> callflowNamespaces) {
this.callflowNamespaces = callflowNamespaces
}

Map<String, Object> getAdditionalConfiguration() {
return this.additionalConfiguration
}

@DataBoundSetter
void setAdditionalConfiguration(final Map<String, Object> additionalConfiguration) {
this.additionalConfiguration = additionalConfiguration
}

@Extension
static class DescriptorImpl extends Descriptor<CallflowConfiguration>
{
@Override
String getDisplayName() {
return Messages.IqPolicyEvaluation_callflowConfiguration()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,8 @@ interface IqPolicyEvaluator
Boolean getEnableDebugLogging()

String getAdvancedProperties()

Boolean getRunCallflow()

CallflowConfiguration getCallflowConfiguration();
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ class IqPolicyEvaluatorBuildStep

String advancedProperties

Boolean runCallflow

CallflowConfiguration callflowConfiguration

@DataBoundConstructor
@SuppressWarnings('ParameterCount')
IqPolicyEvaluatorBuildStep(final String iqInstanceId,
Expand All @@ -70,7 +74,10 @@ class IqPolicyEvaluatorBuildStep
final Boolean failBuildOnScanningErrors,
final String jobCredentialsId,
final Boolean enableDebugLogging,
final String advancedProperties)
final String advancedProperties,
final Boolean runCallflow,
final CallflowConfiguration callflowConfiguration
)
{
this.iqInstanceId = iqInstanceId
this.jobCredentialsId = jobCredentialsId
Expand All @@ -83,6 +90,8 @@ class IqPolicyEvaluatorBuildStep
this.iqApplication = iqApplication
this.advancedProperties = advancedProperties
this.enableDebugLogging = enableDebugLogging
this.runCallflow = runCallflow
this.callflowConfiguration = callflowConfiguration
}

@DataBoundSetter
Expand Down Expand Up @@ -201,5 +210,13 @@ class IqPolicyEvaluatorBuildStep
def jobCredentials = jobCredentialsId ?: nxiqConfiguration?.credentialsId
IqUtil.doFillIqOrganizationItems(serverUrl, jobCredentials, job)
}

Boolean getRunCallflow() {
return this.runCallflow
}

CallflowConfiguration getCallflowConfiguration() {
return this.callflowConfiguration
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ package org.sonatype.nexus.ci.iq

import java.nio.file.Paths

import com.sonatype.nexus.api.common.CallflowOptions
import com.sonatype.nexus.api.exception.IqClientException
import com.sonatype.nexus.api.iq.ApplicationPolicyEvaluation

Expand All @@ -34,11 +35,12 @@ import org.apache.commons.lang.exception.ExceptionUtils

import static com.google.common.base.Preconditions.checkArgument

@SuppressWarnings(['ClassSize', 'AbcMetric'])
class IqPolicyEvaluatorUtil
{
static final String MINIMAL_SERVER_VERSION_REQUIRED = '1.69.0'

@SuppressWarnings(['AbcMetric', 'ParameterCount', 'CyclomaticComplexity'])
@SuppressWarnings(['ParameterCount', 'CyclomaticComplexity', 'MethodSize'])
static ApplicationPolicyEvaluation evaluatePolicy(final IqPolicyEvaluator iqPolicyEvaluator,
final Run run,
final FilePath workspace,
Expand Down Expand Up @@ -110,7 +112,32 @@ class IqPolicyEvaluatorUtil
}

File workDirectory = new File(workspace.getRemote())
evaluationResult = iqClient.evaluateApplication(applicationId, iqStage, scanResult, workDirectory)

final CallflowOptions callflowOptions

if (iqPolicyEvaluator.getRunCallflow()) {
listener.logger.println('callflow analysis is enabled')

CallflowConfiguration callflowConfiguration = iqPolicyEvaluator.getCallflowConfiguration()

callflowOptions = makeCallflowOptions(
callflowConfiguration,
remoteScanner,
workDirectory,
envVars,
iqPolicyEvaluator.iqScanPatterns
)
} else {
callflowOptions = null
}

evaluationResult = iqClient.evaluateApplication(
applicationId,
iqStage,
scanResult,
workDirectory,
callflowOptions as CallflowOptions
)
} finally {
// clean up scan files on master and agent
scanResult?.scanFile?.delete()
Expand All @@ -125,7 +152,7 @@ class IqPolicyEvaluatorUtil
def reportAction = new PolicyEvaluationReportAction(applicationId, iqStage, run, evaluationResult)
run.addAction(reportAction)
}

Result result = handleEvaluationResult(evaluationResult, listener, applicationId, iqConfig?.hideReports)
run.setResult(result)
if (result == Result.FAILURE) {
Expand Down Expand Up @@ -184,6 +211,46 @@ class IqPolicyEvaluatorUtil
iqScanPatterns.collect { envVars.expand(it.scanPattern) } - null - ''
}

private static CallflowOptions makeCallflowOptions(
final CallflowConfiguration callflowConfiguration,
final RemoteScanner remoteScanner,
final File workdir,
final EnvVars envVars,
final List<ScanPattern> iqScanPatterns)
{
if (callflowConfiguration == null) {
final List<String> expandedPatterns = getScanPatterns(iqScanPatterns, envVars)
final List<String> targets = remoteScanner.getScanTargets(workdir, expandedPatterns)
.collect {
return it.getAbsolutePath()
}

// defaults to using same targets as original iq scan, when enabled but no additional config passed
return new CallflowOptions(targets, null, null)
} else {
List<ScanPattern> patterns = callflowConfiguration.getCallflowScanPatterns()
if (patterns == null) {
// defaults to using same targets as original iq scan, when no patterns passed with additional config
patterns = iqScanPatterns
}

final List<String> expandedPatterns = getScanPatterns(patterns, envVars)

final List<String> targets = remoteScanner.getScanTargets(workdir, expandedPatterns)
.collect { it.getAbsolutePath() }

final Properties addtionalConfiguration = new Properties()
if (callflowConfiguration.getAdditionalConfiguration() != null) {
addtionalConfiguration.putAll(callflowConfiguration.getAdditionalConfiguration())
}

return new CallflowOptions(
targets,
callflowConfiguration.getCallflowNamespaces(),
addtionalConfiguration)
}
}

private static List<String> getExpandedModuleExcludes(final List<ModuleExclude> moduleExcludes,
final EnvVars envVars)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ class IqPolicyEvaluatorWorkflowStep

String advancedProperties

Boolean runCallflow

CallflowConfiguration callflowConfiguration;

@DataBoundSetter
void setIqOrganization(final String iqOrganization) {
this.iqOrganization = iqOrganization
Expand Down Expand Up @@ -89,6 +93,16 @@ class IqPolicyEvaluatorWorkflowStep
this.enableDebugLogging = enableDebugLogging
}

@DataBoundSetter
void setRunCallflow(Boolean runCallflow) {
this.runCallflow = runCallflow
}

@DataBoundSetter
void setCallflowConfiguration(final CallflowConfiguration callflowConfiguration) {
this.callflowConfiguration = callflowConfiguration
}

@DataBoundSetter
void setAdvancedProperties(final String advancedProperties) {
this.advancedProperties = advancedProperties
Expand Down Expand Up @@ -124,6 +138,16 @@ class IqPolicyEvaluatorWorkflowStep
this.iqInstanceId = iqInstanceId ?: IqUtil.getFirstIqConfiguration()?.id
}

@Override
Boolean getRunCallflow() {
return this.runCallflow
}

@Override
CallflowConfiguration getCallflowConfiguration() {
return this.callflowConfiguration
}

@Extension
static final class DescriptorImpl
extends AbstractStepDescriptorImpl
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ IqPolicyEvaluation.NoViolations=No Violations
IqPolicyEvaluation.LegacyViolations={0} Legacy {0,choice,0#Violations|1#Violation|1< Violations}
IqPolicyEvaluation.TotalViolations={0} Violations
IqPolicyEvaluation.AffectedComponents=affecting {0} out of {1} components evaluated
IqPolicyEvaluation.runCallflow=Flag indicating if callflow analysis should be ran
IqPolicyEvaluation.callflowConfiguration=Additional callflow configuration options

PolicyFailureMessageFormatter.PolicyFailing=Nexus IQ reports policy failing due to {0}
PolicyFailureMessageFormatter.PolicyWarning=Nexus IQ reports policy warning due to {0}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ class ComToOrgMigratorIntegrationTest
1 * iqClient.verifyOrCreateApplication(*_) >> true
1 * iqClient.getLicensedFeatures() >> []
1 * iqClient.scan(*_) >> new ScanResult(new Scan(), File.createTempFile('dummy-scan', '.xml.gz'))
1 * iqClient.evaluateApplication('sample-app', 'build', _, _) >> new ApplicationPolicyEvaluation(
1 * iqClient.evaluateApplication('sample-app', 'build', *_) >> new ApplicationPolicyEvaluation(
0, 1, 2, 3, 11, 12, 13, 0, 1, [], 'http://server/link/to/report')

then: 'the return code is successful'
Expand All @@ -124,7 +124,7 @@ class ComToOrgMigratorIntegrationTest
then: 'the application is scanned and evaluated'
1 * iqClient.verifyOrCreateApplication(*_) >> true
1 * iqClient.scan(*_) >> new ScanResult(new Scan(), File.createTempFile('dummy-scan', '.xml.gz'))
1 * iqClient.evaluateApplication('sample-app', 'build', _, _) >> new ApplicationPolicyEvaluation(
1 * iqClient.evaluateApplication('sample-app', 'build', *_) >> new ApplicationPolicyEvaluation(
0, 1, 2, 3, 11, 12, 13, 0, 1, [], 'http://server/link/to/report')

then: 'the expected result is returned'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ abstract class IqPolicyEvaluatorDescriptorTest

when:
def buildStep = new IqPolicyEvaluatorBuildStep(null, null, null, null, null, null, null, null,
'jobSpecificCredentialsId', null, null)
'jobSpecificCredentialsId', null, null, false, null)

then:
buildStep.jobCredentialsId == 'jobSpecificCredentialsId'
Expand Down
Loading

0 comments on commit fe854a6

Please sign in to comment.