Skip to content
This repository was archived by the owner on Oct 2, 2025. It is now read-only.

Commit 04f380b

Browse files
feat(jenkins): Enable Jenkins job triggers for jobs in sub-folders (#1373) (#1382)
- Allow triggering of Jenkins jobs that reside in sub-folders, by treating job names containing slashes as query variables. - Prior to this feature, jobs in sub-folders were not appropriately matched by the Spring framework due to slashes in their path, causing trigger requests to fail. - Include a new feature flag to determine the usage of the existing endpoint (which uses the job name as a path variable) or an updated endpoint (which takes the job name as a query parameter). (cherry picked from commit 18d038d) Co-authored-by: Raul Cristian <[email protected]>
1 parent 953c35d commit 04f380b

File tree

5 files changed

+84
-7
lines changed

5 files changed

+84
-7
lines changed

echo-core/src/main/java/com/netflix/spinnaker/echo/build/BuildInfoService.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,18 @@
1818

1919
import com.fasterxml.jackson.core.type.TypeReference;
2020
import com.fasterxml.jackson.databind.ObjectMapper;
21+
import com.netflix.spinnaker.echo.config.IgorConfigurationProperties;
2122
import com.netflix.spinnaker.echo.jackson.EchoObjectMapper;
2223
import com.netflix.spinnaker.echo.model.Trigger;
2324
import com.netflix.spinnaker.echo.model.trigger.BuildEvent;
2425
import com.netflix.spinnaker.echo.services.IgorService;
2526
import com.netflix.spinnaker.kork.artifacts.model.Artifact;
2627
import com.netflix.spinnaker.kork.core.RetrySupport;
27-
import java.util.*;
28+
import java.util.ArrayList;
29+
import java.util.Collections;
30+
import java.util.List;
31+
import java.util.Map;
32+
import java.util.Optional;
2833
import java.util.function.Supplier;
2934
import lombok.RequiredArgsConstructor;
3035
import org.apache.commons.lang3.StringUtils;
@@ -40,6 +45,7 @@
4045
public class BuildInfoService {
4146
private final IgorService igorService;
4247
private final RetrySupport retrySupport;
48+
private final IgorConfigurationProperties igorConfigurationProperties;
4349
private final ObjectMapper objectMapper = EchoObjectMapper.getInstance();
4450

4551
// Manual triggers try to replicate actual events (and in some cases build events) but rather than
@@ -49,7 +55,11 @@ public class BuildInfoService {
4955
// an event as with other triggers, but for now we'll see whether we can extract a build event
5056
// from the trigger.
5157
public BuildEvent getBuildEvent(String master, String job, int buildNumber) {
52-
Map<String, Object> rawBuild = retry(() -> igorService.getBuild(buildNumber, master, job));
58+
Map<String, Object> rawBuild =
59+
retry(
60+
igorConfigurationProperties.isJobNameAsQueryParameter()
61+
? () -> igorService.getBuildStatusWithJobQueryParameter(buildNumber, master, job)
62+
: () -> igorService.getBuild(buildNumber, master, job));
5363
BuildEvent.Build build = objectMapper.convertValue(rawBuild, BuildEvent.Build.class);
5464
BuildEvent.Project project = new BuildEvent.Project(job, build);
5565
BuildEvent.Content content = new BuildEvent.Content(project, master);
@@ -64,7 +74,11 @@ public Map<String, Object> getBuildInfo(BuildEvent event) {
6474
int buildNumber = event.getBuildNumber();
6575

6676
if (StringUtils.isNoneEmpty(master, job)) {
67-
return retry(() -> igorService.getBuild(buildNumber, master, job));
77+
return retry(
78+
() ->
79+
igorConfigurationProperties.isJobNameAsQueryParameter()
80+
? igorService.getBuildStatusWithJobQueryParameter(buildNumber, master, job)
81+
: igorService.getBuild(buildNumber, master, job));
6882
}
6983
return Collections.emptyMap();
7084
}

echo-core/src/main/java/com/netflix/spinnaker/echo/config/BuildInfoConfig.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,19 @@
2121
import com.netflix.spinnaker.echo.services.IgorService;
2222
import com.netflix.spinnaker.kork.core.RetrySupport;
2323
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
24+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
2425
import org.springframework.context.annotation.Bean;
2526
import org.springframework.context.annotation.Configuration;
2627

2728
@Configuration
2829
@ConditionalOnProperty("igor.enabled")
30+
@EnableConfigurationProperties(IgorConfigurationProperties.class)
2931
public class BuildInfoConfig {
3032
@Bean
31-
BuildInfoService buildInfoService(IgorService igorService, RetrySupport retrySupport) {
32-
return new BuildInfoService(igorService, retrySupport);
33+
BuildInfoService buildInfoService(
34+
IgorService igorService,
35+
RetrySupport retrySupport,
36+
IgorConfigurationProperties igorConfigurationProperties) {
37+
return new BuildInfoService(igorService, retrySupport, igorConfigurationProperties);
3338
}
3439
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2023 Netflix, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.netflix.spinnaker.echo.config;
18+
19+
import lombok.Getter;
20+
import lombok.Setter;
21+
import org.springframework.boot.context.properties.ConfigurationProperties;
22+
23+
@Setter
24+
@Getter
25+
@ConfigurationProperties(prefix = "feature.igor")
26+
public class IgorConfigurationProperties {
27+
private boolean jobNameAsQueryParameter = false;
28+
}

echo-core/src/main/java/com/netflix/spinnaker/echo/services/IgorService.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.netflix.spinnaker.kork.artifacts.model.Artifact;
2020
import java.util.List;
2121
import java.util.Map;
22+
import org.jetbrains.annotations.NotNull;
2223
import retrofit.client.Response;
2324
import retrofit.http.*;
2425
import retrofit.mime.TypedInput;
@@ -30,6 +31,12 @@ Map<String, Object> getBuild(
3031
@Path("master") String master,
3132
@Path(value = "job", encode = false) String job);
3233

34+
@GET("/builds/status/{buildNumber}/{master}")
35+
Map<String, Object> getBuildStatusWithJobQueryParameter(
36+
@NotNull @Path("buildNumber") Integer buildNumber,
37+
@NotNull @Path("master") String master,
38+
@NotNull @Query(value = "job") String job);
39+
3340
@GET("/builds/properties/{buildNumber}/{fileName}/{master}/{job}")
3441
Map<String, Object> getPropertyFile(
3542
@Path("buildNumber") Integer buildNumber,

echo-pipelinetriggers/src/test/groovy/com/netflix/spinnaker/echo/pipelinetriggers/eventhandlers/BuildEventHandlerSpec.groovy

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package com.netflix.spinnaker.echo.pipelinetriggers.eventhandlers
22

3-
import com.fasterxml.jackson.databind.ObjectMapper
43
import com.netflix.spectator.api.NoopRegistry
54
import com.netflix.spinnaker.echo.build.BuildInfoService
5+
import com.netflix.spinnaker.echo.config.IgorConfigurationProperties
66
import com.netflix.spinnaker.echo.jackson.EchoObjectMapper
77
import com.netflix.spinnaker.echo.model.Pipeline
88
import com.netflix.spinnaker.echo.model.trigger.BuildEvent
@@ -20,7 +20,7 @@ class BuildEventHandlerSpec extends Specification implements RetrofitStubs {
2020
def registry = new NoopRegistry()
2121
def objectMapper = EchoObjectMapper.getInstance()
2222
def igorService = Mock(IgorService)
23-
def buildInformation = new BuildInfoService(igorService, new RetrySupport())
23+
def buildInformation = new BuildInfoService(igorService, new RetrySupport(), new IgorConfigurationProperties(jobNameAsQueryParameter: false))
2424
def handlerSupport = new EventHandlerSupport()
2525
def fiatPermissionEvaluator = Mock(FiatPermissionEvaluator)
2626

@@ -246,6 +246,29 @@ class BuildEventHandlerSpec extends Specification implements RetrofitStubs {
246246
outputTrigger.buildInfo.equals(BUILD_INFO)
247247
}
248248

249+
def "getBuildInfo method gets job name from query when flag is true"()
250+
{
251+
given:
252+
def mockBuildInfo = BUILD_INFO
253+
def trigger = enabledJenkinsTrigger.withMaster(MASTER_NAME).withBuildNumber(BUILD_NUMBER)
254+
createPipelineWith(enabledJenkinsTrigger).withTrigger(trigger)
255+
def event = getBuildEvent()
256+
257+
def retrySupport = new RetrySupport()
258+
def configProperties = new IgorConfigurationProperties(jobNameAsQueryParameter: true)
259+
def buildInfoService = new BuildInfoService(igorService, retrySupport, configProperties)
260+
261+
def permissionEvaluator = fiatPermissionEvaluator
262+
def buildEventHandler = new BuildEventHandler(registry, objectMapper, Optional.of(buildInfoService), permissionEvaluator)
263+
264+
when:
265+
def outputTrigger = buildEventHandler.buildTrigger(event).apply(trigger)
266+
267+
then:
268+
1 * igorService.getBuildStatusWithJobQueryParameter(BUILD_NUMBER, MASTER_NAME, JOB_NAME) >> mockBuildInfo
269+
outputTrigger.buildInfo == mockBuildInfo
270+
}
271+
249272
def "fetches property file if defined"() {
250273
given:
251274
def trigger = enabledJenkinsTrigger

0 commit comments

Comments
 (0)