Skip to content

Commit 3c66695

Browse files
authored
Fix SonarQube run enable/disable logic (#1261)
1 parent b14a4b3 commit 3c66695

File tree

4 files changed

+88
-64
lines changed

4 files changed

+88
-64
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
* Add component information in automatic release close notes ([#1254](https://github.com/opendevstack/ods-jenkins-shared-library/pull/1254))
1111

1212
### Fixed
13+
* Fix SonarQube run enable/disable logic ([#1259](https://github.com/opendevstack/ods-jenkins-shared-library/pull/1259))
1314
* Log correct error message for wrong preview-branch value ([#1249](https://github.com/opendevstack/ods-jenkins-shared-library/pull/1249))
1415
* Fail the pipeline when no version specified for a deploy to Q, P ([#1248](https://github.com/opendevstack/ods-jenkins-shared-library/pull/1248))
1516
* Fix Tailor deployment drifts for D, Q envs ([#1055](https://github.com/opendevstack/ods-jenkins-shared-library/pull/1055))

src/org/ods/component/AutomaticSonarScanner.groovy

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -66,22 +66,18 @@ class AutomaticSonarScanner implements Serializable {
6666
"Parameter 'projects.${context.projectId}.enabled' at cluster level exists"
6767
)
6868
} else {
69-
configurationSonarProject.put('enabled', true)
7069
logger.info(
71-
"Not parameter 'projects.${context.projectId}.enabled' at cluster level. " +
72-
"Default enabled"
70+
"Not parameter 'projects.${context.projectId}.enabled' at cluster level."
7371
)
7472
}
7573

76-
boolean enabledInCluster = Boolean.valueOf(
77-
configurationSonarCluster['enabled']?.toString() ?: "true"
78-
)
79-
boolean enabledInProject = Boolean.valueOf(
80-
configurationSonarProject['enabled']?.toString() ?: "true"
81-
)
74+
// Project config takes precedence: if project is explicitly configured, use its value
75+
// Otherwise, fall back to cluster config
76+
boolean shouldRun = configurationSonarProject.isEmpty() ?
77+
Boolean.valueOf(configurationSonarCluster['enabled']?.toString() ?: "true") :
78+
Boolean.valueOf(configurationSonarProject['enabled']?.toString() ?: "true")
8279

83-
// Only run scan if enabled in both cluster and project
84-
if (enabledInCluster && enabledInProject) {
80+
if (shouldRun) {
8581
Stage sonarStage = new ScanWithSonarStage(
8682
script,
8783
context,
@@ -96,20 +92,14 @@ class AutomaticSonarScanner implements Serializable {
9692
sonarStage.execute()
9793
logger.info("Automatic SonarQube scan completed successfully")
9894
} else {
99-
if (!enabledInCluster && !enabledInProject) {
100-
logger.warn(
101-
"Skipping SonarQube scan because it is not enabled at cluster nor " +
102-
"project level"
103-
)
104-
} else if (enabledInCluster) {
105-
logger.warn(
106-
"Skipping SonarQube scan because it is not enabled at project level"
107-
)
108-
} else {
109-
logger.warn(
110-
"Skipping SonarQube scan because it is not enabled at cluster level"
111-
)
112-
}
95+
String reason = !configurationSonarProject.isEmpty() ?
96+
"project is not enabled" :
97+
"is not enabled at cluster level"
98+
logger.warn(
99+
"Skipping SonarQube scan because $reason " +
100+
"in '${ScanWithSonarStage.SONAR_CONFIG_MAP_NAME}' ConfigMap " +
101+
"in ${config.odsNamespace.OPENSHIFT_BUILD_NAMESPACE} project"
102+
)
113103
}
114104
}
115105

test/groovy/vars/OdsComponentStageScanWithSonarSpec.groovy

Lines changed: 58 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -117,29 +117,66 @@ class OdsComponentStageScanWithSonarSpec extends PipelineSpockTestBase {
117117
assertJobStatusSuccess()
118118
}
119119

120-
def "skips scan if not enabled in cluster and project"() {
121-
given:
122-
def c = config + [environment: 'dev']
123-
IContext context = new Context(null, c, logger)
124-
BitbucketService bitbucketService = Stub(BitbucketService.class)
125-
ServiceRegistry.instance.add(BitbucketService, bitbucketService)
126-
NexusService nexusService = Mock(NexusService.class)
127-
ServiceRegistry.instance.add(NexusService, nexusService)
128-
SonarQubeService sonarQubeService = Stub(SonarQubeService.class)
129-
ServiceRegistry.instance.add(SonarQubeService, sonarQubeService)
130-
def script = loadScript('vars/odsComponentStageScanWithSonar.groovy')
131-
script.env.OPENSHIFT_BUILD_NAMESPACE = 'test-namespace'
132-
helper.registerAllowedMethod('emailext', [Map]) { Map args -> }
133-
// Mock sh to return config map with enabled=false
134-
helper.registerAllowedMethod('sh', [Map]) { Map args -> '{"data": {"enabled": "false", "alertEmails": "test@example.com"}}' }
135-
when:
136-
script.call(context)
137-
then:
138-
printCallStack()
139-
assertCallStackContains('SonarQube scan not enabled at cluster level')
140-
assertJobStatusSuccess()
120+
@Unroll
121+
def "enable/disable scan: clusterEnabled=#clusterEnabled, projectEnabled=#projectEnabled"() {
122+
// The configmap key for project-level override is "projects.<projectId>.enabled".
123+
// The shared config has projectId='foo', so the key is "projects.foo.enabled".
124+
// Project value always governs when the key is present; cluster value is ignored.
125+
given:
126+
def c = config + [environment: 'dev']
127+
IContext context = new Context(null, c, logger)
128+
BitbucketService bitbucketService = Stub(BitbucketService.class)
129+
bitbucketService.findPullRequest(*_) >> [:]
130+
ServiceRegistry.instance.add(BitbucketService, bitbucketService)
131+
NexusService nexusService = Mock(NexusService.class)
132+
ServiceRegistry.instance.add(NexusService, nexusService)
133+
SonarQubeService sonarQubeService = Stub(SonarQubeService.class)
134+
sonarQubeService.readProperties() >> ['sonar.projectKey': 'foo']
135+
sonarQubeService.readTask() >> ['ceTaskId': 'AXxaAoUSsjAMlIY9kNmn']
136+
sonarQubeService.scan(*_) >> null
137+
sonarQubeService.getQualityGateJSON(*_) >> '{"projectStatus":{"status":"OK"}}'
138+
sonarQubeService.getComputeEngineTaskResult(*_) >> 'SUCCESS'
139+
sonarQubeService.getSonarQubeHostUrl() >> 'https://sonarqube.example.com'
140+
ServiceRegistry.instance.add(SonarQubeService, sonarQubeService)
141+
142+
when:
143+
def script = loadScript('vars/odsComponentStageScanWithSonar.groovy')
144+
script.env.WORKSPACE = tempFolder.getRoot().absolutePath
145+
script.env.OPENSHIFT_BUILD_NAMESPACE = 'test-namespace'
146+
helper.registerAllowedMethod('archiveArtifacts', [Map]) { Map args -> }
147+
helper.registerAllowedMethod('stash', [Map]) { Map args -> }
148+
helper.registerAllowedMethod('readFile', [Map]) { Map args -> '' }
149+
helper.registerAllowedMethod('emailext', [Map]) { Map args -> }
150+
helper.registerAllowedMethod('sh', [Map]) { Map args -> configMapJson }
151+
script.call(context)
152+
153+
then:
154+
printCallStack()
155+
assertJobStatusSuccess()
156+
if (shouldScan) {
157+
assertCallStackContains('SonarQube Analysis')
158+
} else {
159+
assertCallStackContains(skipReason)
160+
assertCallStackContains('SonarQube scan not enabled')
141161
}
142162

163+
where:
164+
// Project value always takes precedence; cluster value is irrelevant when project key is present.
165+
clusterEnabled | projectEnabled || configMapJson | shouldScan | skipReason
166+
'true' | 'true' || '{"data": {"enabled": "true", "projects.foo.enabled": "true"}}' | true | ''
167+
'true' | 'false' || '{"data": {"enabled": "true", "projects.foo.enabled": "false"}}' | false | 'Skipping SonarQube scan because project is not enabled'
168+
// Project enabled=true overrides a disabled cluster → scan MUST run
169+
'false' | 'true' || '{"data": {"enabled": "false", "projects.foo.enabled": "true"}}' | true | ''
170+
// Project disabled regardless of cluster state → scan MUST NOT run
171+
'false' | 'false' || '{"data": {"enabled": "false", "projects.foo.enabled": "false"}}' | false | 'Skipping SonarQube scan because project is not enabled'
172+
// Cluster enabled not explicitly set (defaults true); project value still governs
173+
'not set' | 'true' || '{"data": {"projects.foo.enabled": "true"}}' | true | ''
174+
'not set' | 'false' || '{"data": {"projects.foo.enabled": "false"}}' | false | 'Skipping SonarQube scan because project is not enabled'
175+
// Project not set (empty map) → falls back to cluster configuration
176+
'true' | 'not set' || '{"data": {"enabled": "true"}}' | true | ''
177+
'false' | 'not set' || '{"data": {"enabled": "false"}}' | false | 'Skipping SonarQube scan because is not enabled at cluster level'
178+
}
179+
143180
@Unroll
144181
def "checks quality gate"() {
145182
given:

vars/odsComponentStageScanWithSonar.groovy

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,11 @@ private List fetchSonarConfig(OpenShiftService openShiftService, IContext contex
103103
Map configurationSonarProject = [:]
104104
def key = "projects." + context.projectId + ".enabled"
105105
if (configurationSonarCluster.containsKey(key)) {
106+
// Store the actual project-level configuration
106107
configurationSonarProject.put('enabled', configurationSonarCluster.get(key))
107108
logger.info "Parameter 'projects.${context.projectId}.enabled' at cluster level exists"
108109
} else {
109-
configurationSonarProject.put('enabled', true)
110-
logger.info "Not parameter 'projects.${context.projectId}.enabled' at cluster level. Default enabled"
110+
logger.info "Not parameter 'projects.${context.projectId}.enabled' at cluster level."
111111
}
112112
return [configurationSonarCluster, configurationSonarProject, alertEmails]
113113
}
@@ -120,12 +120,15 @@ private String handleSonarScan(
120120
Map services // expects keys: bitbucketService, sonarQubeService, nexusService, logger
121121
) {
122122
String errorMessages = ''
123-
boolean enabledInCluster = Boolean.valueOf(configurationSonarCluster['enabled']?.toString() ?: "true")
124-
boolean enabledInProject = Boolean.valueOf(configurationSonarProject['enabled']?.toString() ?: "true")
125123
if (configurationSonarCluster['nexusRepository']) {
126124
config.sonarQubeNexusRepository = configurationSonarCluster['nexusRepository']
127125
}
128-
if (enabledInCluster && enabledInProject) {
126+
// Project config takes precedence: if project is explicitly configured, use its value
127+
// Otherwise, fall back to cluster config
128+
boolean shouldRun = configurationSonarProject.isEmpty() ?
129+
Boolean.valueOf(configurationSonarCluster['enabled']?.toString() ?: "true") :
130+
Boolean.valueOf(configurationSonarProject['enabled']?.toString() ?: "true")
131+
if (shouldRun) {
129132
new ScanWithSonarStage(
130133
this,
131134
context,
@@ -137,21 +140,14 @@ private String handleSonarScan(
137140
configurationSonarCluster,
138141
configurationSonarProject
139142
).execute()
140-
} else if (!enabledInCluster && !enabledInProject) {
141-
services.logger.warn("Skipping SonarQube scan because is not enabled nor cluster nor project " +
143+
} else {
144+
String reason = !configurationSonarProject.isEmpty() ?
145+
"project is not enabled" :
146+
"is not enabled at cluster level"
147+
services.logger.warn("Skipping SonarQube scan because $reason " +
142148
"in '${ScanWithSonarStage.SONAR_CONFIG_MAP_NAME}' ConfigMap " +
143149
"in ${config.odsNamespace.OPENSHIFT_BUILD_NAMESPACE} project")
144-
errorMessages += "<li>SonarQube scan not enabled at cluster nor project level</li>"
145-
} else if (enabledInCluster) {
146-
services.logger.warn("Skipping SonarQube scan because is not enabled at project level " +
147-
"in '${ScanWithSonarStage.SONAR_CONFIG_MAP_NAME}' " +
148-
"ConfigMap in ${config.odsNamespace.OPENSHIFT_BUILD_NAMESPACE} project")
149-
errorMessages += "<li>SonarQube scan not enabled at project level</li>"
150-
} else {
151-
services.logger.warn("Skipping SonarQube scan because is not enabled at cluster level " +
152-
"in '${ScanWithSonarStage.SONAR_CONFIG_MAP_NAME}' " +
153-
"ConfigMap in ${config.odsNamespace.OPENSHIFT_BUILD_NAMESPACE} project")
154-
errorMessages += "<li>SonarQube scan not enabled at cluster level</li>"
150+
errorMessages += "<li>SonarQube scan not enabled</li>"
155151
}
156152
return errorMessages
157153
}

0 commit comments

Comments
 (0)