Skip to content

Commit 51ebe94

Browse files
authored
Parameters in cron triggers (#801)
* feat(cron): pass along cron trigger parameters to orca Triggers support a parameters map, but for cron triggers it gets lost along the way and never makes it to orca, meaning that only default parameter values can be used for cron triggers. To work around this, users have been duplicating pipelines with different default values and different triggers. This does not scale for obvious reasons and is just generally cumbersome.
1 parent b0e1fc2 commit 51ebe94

File tree

5 files changed

+48
-38
lines changed

5 files changed

+48
-38
lines changed

echo-core/src/main/java/com/netflix/spinnaker/echo/discovery/DiscoveryActivated.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import org.springframework.context.ApplicationListener;
2525

2626
/**
27-
* A component that starts doing something when the instance is up in discovery and stops doing that
27+
* A component that starts doing something when the instance is up in Eureka and stops doing that
2828
* thing when it goes down.
2929
*/
3030
public interface DiscoveryActivated extends ApplicationListener<RemoteStatusChangedEvent> {

echo-pubsub-aws/src/main/java/com/netflix/spinnaker/echo/pubsub/aws/SQSSubscriberProvider.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.amazonaws.services.sns.AmazonSNSClientBuilder;
2222
import com.amazonaws.services.sqs.AmazonSQSClientBuilder;
2323
import com.fasterxml.jackson.databind.ObjectMapper;
24+
import com.google.common.base.Preconditions;
2425
import com.netflix.spectator.api.Registry;
2526
import com.netflix.spinnaker.echo.artifacts.MessageArtifactTranslator;
2627
import com.netflix.spinnaker.echo.config.AmazonPubsubProperties;
@@ -78,9 +79,8 @@ public class SQSSubscriberProvider implements DiscoveryActivated {
7879

7980
@PostConstruct
8081
public void start() {
81-
if (properties == null) {
82-
return;
83-
}
82+
Preconditions.checkNotNull(
83+
properties, "Can't initialize SQSSubscriberProvider with null properties");
8484

8585
ExecutorService executorService =
8686
Executors.newFixedThreadPool(properties.getSubscriptions().size());

echo-pubsub-google/src/main/java/com/netflix/spinnaker/echo/pubsub/google/GooglePubsubPublisher.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public static GooglePubsubPublisher buildPublisher(
8484
.build();
8585
publisher.setPublisher(p);
8686
} catch (IOException ioe) {
87-
log.error("Could not create Google Pubsub Publishers: {}", ioe);
87+
log.error("Could not create Google Pubsub Publishers", ioe);
8888
}
8989

9090
return publisher;
@@ -95,7 +95,7 @@ public void publishEvent(Event event) {
9595
try {
9696
jsonPayload = mapper.writeValueAsString(event);
9797
} catch (JsonProcessingException jpe) {
98-
log.error("Could not serialize event message: {}", jpe);
98+
log.error("Could not serialize event message", jpe);
9999
return;
100100
}
101101

@@ -146,7 +146,7 @@ public void publish(Map payload, Map<String, String> attributes) {
146146
try {
147147
jsonPayload = mapper.writeValueAsString(payload);
148148
} catch (JsonProcessingException jpe) {
149-
log.error("Could not serialize event message: {}", jpe);
149+
log.error("Could not serialize event message", jpe);
150150
return;
151151
}
152152
publish(jsonPayload, attributes);

echo-scheduler/src/main/groovy/com/netflix/spinnaker/echo/scheduler/actions/pipeline/TriggerConverter.groovy

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import static org.quartz.TriggerBuilder.newTrigger
3232
class TriggerConverter {
3333
public static final String JOB_ID = "Pipeline Trigger"
3434

35-
static Map<String, String> toParamMap(Trigger trigger, String timeZoneId) {
35+
static Map<String, Object> toParamMap(Trigger trigger, String timeZoneId) {
3636
def params = [
3737
id : trigger.parent.id,
3838
application : trigger.parent.application,
@@ -41,7 +41,8 @@ class TriggerConverter {
4141
triggerCronExpression: trigger.cronExpression,
4242
triggerTimeZoneId : timeZoneId,
4343
triggerRebake : Boolean.toString(trigger.rebake),
44-
triggerEnabled : "true"
44+
triggerEnabled : "true",
45+
triggerParameters : trigger.parameters
4546
]
4647

4748
if (trigger.runAsUser) {
@@ -77,22 +78,23 @@ class TriggerConverter {
7778
}
7879

7980
static Pipeline toPipeline(PipelineCache pipelineCache, Map<String, Object> parameters) {
81+
def existingPipeline = pipelineCache.getPipelinesSync().find { it.id == parameters.id }
82+
if (!existingPipeline) {
83+
throw new IllegalStateException("No pipeline found (id: ${parameters.id})")
84+
}
85+
8086
def triggerBuilder = Trigger
8187
.builder()
82-
.enabled(Boolean.parseBoolean(parameters.triggerEnabled))
83-
.rebake(Boolean.parseBoolean(parameters.triggerRebake))
84-
.id(parameters.triggerId)
88+
.enabled(Boolean.parseBoolean(parameters.triggerEnabled as String))
89+
.rebake(Boolean.parseBoolean(parameters.triggerRebake as String))
90+
.id(parameters.triggerId as String)
8591
.type(Trigger.Type.CRON.toString())
8692
.eventId(UUID.randomUUID().toString())
87-
.cronExpression(parameters.triggerCronExpression)
93+
.cronExpression(parameters.triggerCronExpression as String)
94+
.parameters(parameters.triggerParameters as Map)
8895

8996
if (parameters.runAsUser) {
90-
triggerBuilder.runAsUser(parameters.runAsUser)
91-
}
92-
93-
def existingPipeline = pipelineCache.getPipelinesSync().find { it.id == parameters.id }
94-
if (!existingPipeline) {
95-
throw new IllegalStateException("No pipeline found (id: ${parameters.id})")
97+
triggerBuilder.runAsUser(parameters.runAsUser as String)
9698
}
9799

98100
return existingPipeline.withTrigger(triggerBuilder.build())
@@ -101,15 +103,9 @@ class TriggerConverter {
101103
static boolean isInSync(CronTrigger trigger, Trigger pipelineTrigger, TimeZone timeZoneId) {
102104
CronTrigger other = toQuartzTrigger(pipelineTrigger, timeZoneId) as CronTrigger
103105

104-
boolean cronExpressionMismatch = (trigger.cronExpression != other.cronExpression)
105-
boolean timezoneMismatch = !trigger.timeZone.hasSameRules(other.timeZone)
106-
boolean runAsUserMismatch =
107-
(trigger.jobDataMap.getString("runAsUser") != other.jobDataMap.getString("runAsUser"))
108-
109-
if (cronExpressionMismatch || runAsUserMismatch || timezoneMismatch) {
110-
return false
111-
}
112-
113-
return true
106+
return (trigger.cronExpression == other.cronExpression
107+
&& trigger.timeZone.hasSameRules(other.timeZone)
108+
&& trigger.jobDataMap.getString("runAsUser") == other.jobDataMap.getString("runAsUser")
109+
&& trigger.jobDataMap.getWrappedMap().triggerParameters == other.jobDataMap.getWrappedMap().triggerParameters)
114110
}
115111
}

echo-scheduler/src/test/groovy/com/netflix/spinnaker/echo/scheduler/actions/pipeline/TriggerConverterSpec.groovy

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import com.netflix.spinnaker.echo.model.Trigger
2121
import com.netflix.spinnaker.echo.pipelinetriggers.PipelineCache
2222
import org.quartz.CronScheduleBuilder
2323
import org.quartz.CronTrigger
24+
import org.quartz.JobDataMap
2425
import spock.lang.Shared
2526
import spock.lang.Specification
2627
import spock.lang.Unroll
@@ -47,6 +48,7 @@ class TriggerConverterSpec extends Specification {
4748
.rebake(triggerRebake)
4849
.runAsUser("mr.captain")
4950
.parent(pipeline)
51+
.parameters(triggerParameters)
5052
.build()
5153

5254
when:
@@ -60,10 +62,12 @@ class TriggerConverterSpec extends Specification {
6062
parameters.triggerTimeZoneId == 'America/New_York'
6163
parameters.triggerRebake == Boolean.toString(trigger.rebake)
6264
parameters.runAsUser == 'mr.captain'
65+
parameters.triggerParameters == triggerParameters
6366

6467
where:
6568
triggerId << ['123-456', null]
6669
triggerRebake << [true, false]
70+
triggerParameters << [null, [param1: 'value1', param2: 42]]
6771
}
6872

6973
@Unroll
@@ -79,7 +83,8 @@ class TriggerConverterSpec extends Specification {
7983
triggerType: 'cron',
8084
triggerCronExpression: '* 0/30 * * * ? *',
8185
triggerEnabled: "true",
82-
triggerRebake: triggerRebake
86+
triggerRebake: triggerRebake,
87+
triggerParameters: ['param1': 42]
8388
]
8489

8590
when:
@@ -94,6 +99,7 @@ class TriggerConverterSpec extends Specification {
9499
pipelineWithTrigger.trigger.cronExpression == parameters.triggerCronExpression
95100
pipelineWithTrigger.trigger.enabled == Boolean.valueOf(parameters.triggerEnabled)
96101
pipelineWithTrigger.trigger.rebake == Boolean.valueOf(parameters.triggerRebake)
102+
pipelineWithTrigger.trigger.parameters == parameters.triggerParameters
97103

98104
where:
99105
triggerRebake << ['true', 'false']
@@ -131,30 +137,38 @@ class TriggerConverterSpec extends Specification {
131137
}
132138

133139
@Unroll
134-
void 'isInSync() should return true if cronExpression, timezone of the trigger, and runAsUser match the ActionInstance'() {
140+
void 'isInSync() should return true if cronExpression, timezone of the trigger, runAsUser and trigger parameters match the ActionInstance'() {
135141
setup:
136142
Trigger pipelineTrigger = Trigger.builder()
137143
.id("id1")
138144
.parent(pipeline)
139145
.type(Trigger.Type.CRON.toString())
140146
.cronExpression('* 0/30 * * * ? *')
141147
.runAsUser("batman")
148+
.parameters([param: 'value'])
142149
.build()
150+
143151
org.quartz.Trigger scheduleTrigger = org.quartz.TriggerBuilder.newTrigger()
144152
.withIdentity("ignored", null)
145153
.withSchedule(CronScheduleBuilder.cronSchedule(pipelineTrigger.cronExpression)
146154
.inTimeZone(TimeZone.getTimeZone(actionInstanceTimeZoneId)))
147-
.usingJobData("runAsUser", runAsUser)
155+
.usingJobData(new JobDataMap([
156+
runAsUser: runAsUser,
157+
triggerParameters: parameters
158+
]))
148159
.build()
149160

150161
expect:
151-
isInSync(scheduleTrigger, pipelineTrigger, TimeZone.getTimeZone(currentTimeZoneId)) == expectedInSync
162+
isInSync(scheduleTrigger, pipelineTrigger, TimeZone.getTimeZone('America/New_York')) == expectedInSync
152163

153164
where:
154-
actionInstanceTimeZoneId | currentTimeZoneId | runAsUser | expectedInSync
155-
'America/New_York' | 'America/New_York' | 'batman' | true
156-
'America/Los_Angeles' | 'America/New_York' | 'batman' | false
157-
'' | 'America/New_York' | 'batman' | false
158-
'America/New_York' | 'America/New_York' | 'robin' | false
165+
actionInstanceTimeZoneId | runAsUser | parameters | expectedInSync
166+
'America/New_York' | 'batman' | [param: 'value'] | true
167+
'America/New_York' | 'batman' | [:] | false
168+
'America/New_York' | 'batman' | null | false
169+
'America/New_York' | 'batman' | [param: 'other'] | false
170+
'America/Los_Angeles' | 'batman' | [param: 'value'] | false
171+
'' | 'batman' | [param: 'value'] | false
172+
'America/New_York' | 'robin' | [param: 'value'] | false
159173
}
160174
}

0 commit comments

Comments
 (0)