diff --git a/echo-model/src/main/java/com/netflix/spinnaker/echo/model/Trigger.java b/echo-model/src/main/java/com/netflix/spinnaker/echo/model/Trigger.java index 197234c69..57693a931 100644 --- a/echo-model/src/main/java/com/netflix/spinnaker/echo/model/Trigger.java +++ b/echo-model/src/main/java/com/netflix/spinnaker/echo/model/Trigger.java @@ -18,11 +18,13 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; +import com.netflix.spinnaker.kork.artifacts.model.Artifact; import lombok.Builder; import lombok.ToString; import lombok.Value; import lombok.experimental.Wither; +import java.util.List; import java.util.Map; @JsonDeserialize(builder = Trigger.TriggerBuilder.class) @@ -73,6 +75,7 @@ public String toString() { String secret; String subscriptionName; String pubsubType; + List expectedArtifacts; public Trigger atBuildNumber(final int buildNumber) { return this.toBuilder() diff --git a/echo-pipelinetriggers/src/main/java/com/netflix/spinnaker/echo/pipelinetriggers/monitor/PubsubEventMonitor.java b/echo-pipelinetriggers/src/main/java/com/netflix/spinnaker/echo/pipelinetriggers/monitor/PubsubEventMonitor.java index e4fd73b2b..7f3b8ba67 100644 --- a/echo-pipelinetriggers/src/main/java/com/netflix/spinnaker/echo/pipelinetriggers/monitor/PubsubEventMonitor.java +++ b/echo-pipelinetriggers/src/main/java/com/netflix/spinnaker/echo/pipelinetriggers/monitor/PubsubEventMonitor.java @@ -25,7 +25,9 @@ import com.netflix.spinnaker.echo.model.trigger.PubsubEvent; import com.netflix.spinnaker.echo.model.trigger.TriggerEvent; import com.netflix.spinnaker.echo.pipelinetriggers.PipelineCache; +import com.netflix.spinnaker.kork.artifacts.model.Artifact; import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; import lombok.val; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -33,6 +35,7 @@ import rx.Observable; import rx.functions.Action1; +import java.util.List; import java.util.function.Function; import java.util.function.Predicate; @@ -40,6 +43,7 @@ * Triggers pipelines in _Orca_ when a trigger-enabled pubsub message arrives. */ @Component +@Slf4j public class PubsubEventMonitor extends TriggerMonitor { public static final String PUBSUB_TRIGGER_TYPE = "pubsub"; @@ -96,10 +100,25 @@ protected Predicate matchTriggerFor(final TriggerEvent event) { PubsubEvent pubsubEvent = (PubsubEvent) event; MessageDescription description = pubsubEvent.getContent().getMessageDescription(); - // TODO(jacobkiefer): Need to apply filters specified in triggers here. return trigger -> trigger.getType().equalsIgnoreCase(PUBSUB_TRIGGER_TYPE) && trigger.getPubsubType().equalsIgnoreCase(description.getPubsubType().toString()) - && trigger.getSubscriptionName().equalsIgnoreCase(description.getSubscriptionName()); + && trigger.getSubscriptionName().equalsIgnoreCase(description.getSubscriptionName()) + && anyArtifactsMatchExpected(description.getArtifacts(), trigger); + } + + private Boolean anyArtifactsMatchExpected(List messageArtifacts, Trigger trigger) { + List expectedArtifacts = trigger.getExpectedArtifacts(); + + if (expectedArtifacts == null || expectedArtifacts.isEmpty()) { + return true; + } + + if (messageArtifacts.size() > expectedArtifacts.size()) { + log.warn("Parsed message artifacts (size {}) greater than expected artifacts (size {}), continuing trigger anyway", messageArtifacts.size(), expectedArtifacts.size()); + } + + Predicate expectedArtifactMatch = a -> trigger.getExpectedArtifacts().stream().anyMatch(e -> a.getType().equals(e.getType()) && a.getName().equals(e.getName())); + return messageArtifacts.stream().anyMatch(expectedArtifactMatch); } @Override diff --git a/echo-pipelinetriggers/src/test/groovy/com/netflix/spinnaker/echo/pipelinetriggers/PubsubEventMonitorSpec.groovy b/echo-pipelinetriggers/src/test/groovy/com/netflix/spinnaker/echo/pipelinetriggers/PubsubEventMonitorSpec.groovy index 019baf50f..a9a3b3c09 100644 --- a/echo-pipelinetriggers/src/test/groovy/com/netflix/spinnaker/echo/pipelinetriggers/PubsubEventMonitorSpec.groovy +++ b/echo-pipelinetriggers/src/test/groovy/com/netflix/spinnaker/echo/pipelinetriggers/PubsubEventMonitorSpec.groovy @@ -24,7 +24,9 @@ import com.netflix.spinnaker.echo.model.Event import com.netflix.spinnaker.echo.model.pubsub.PubsubType import com.netflix.spinnaker.echo.pipelinetriggers.monitor.PubsubEventMonitor import com.netflix.spinnaker.echo.test.RetrofitStubs +import com.netflix.spinnaker.kork.artifacts.model.Artifact import rx.functions.Action1 +import spock.lang.Shared import spock.lang.Specification import spock.lang.Subject import spock.lang.Unroll @@ -38,6 +40,8 @@ class PubsubEventMonitorSpec extends Specification implements RetrofitStubs { counter(*_) >> Stub(Counter) gauge(*_) >> Integer.valueOf(1) } + @Shared def goodArtifacts = [new Artifact(name: 'myArtifact', type: 'artifactType')] + @Shared def badArtifacts = [new Artifact(name: 'myBadArtifact', type: 'badArtifactType')] @Subject def monitor = new PubsubEventMonitor(pipelineCache, subscriber, registry) @@ -57,8 +61,11 @@ class PubsubEventMonitorSpec extends Specification implements RetrofitStubs { }) where: - event | trigger - createPubsubEvent(PubsubType.GOOGLE, "projects/project/subscriptions/subscription") | enabledGooglePubsubTrigger + event | trigger + createPubsubEvent(PubsubType.GOOGLE, "projects/project/subscriptions/subscription", null) | enabledGooglePubsubTrigger + createPubsubEvent(PubsubType.GOOGLE, "projects/project/subscriptions/subscription", []) | enabledGooglePubsubTrigger + createPubsubEvent(PubsubType.GOOGLE, "projects/project/subscriptions/subscription", goodArtifacts) | enabledGooglePubsubTrigger.withExpectedArtifacts(goodArtifacts) + createPubsubEvent(PubsubType.GOOGLE, "projects/project/subscriptions/subscription", goodArtifacts) | enabledGooglePubsubTrigger // Trigger doesn't care about artifacts. // TODO(jacobkiefer): Add Kafka cases when that is implemented. } @@ -78,7 +85,7 @@ class PubsubEventMonitorSpec extends Specification implements RetrofitStubs { disabledGooglePubsubTrigger | "disabled Google pubsub trigger" pipeline = createPipelineWith(trigger) - event = createPubsubEvent(PubsubType.GOOGLE, "projects/project/subscriptions/subscription") + event = createPubsubEvent(PubsubType.GOOGLE, "projects/project/subscriptions/subscription", []) // TODO(jacobkiefer): Add Kafka cases when that is implemented. } @@ -97,7 +104,7 @@ class PubsubEventMonitorSpec extends Specification implements RetrofitStubs { }) where: - event = createPubsubEvent(PubsubType.GOOGLE, "projects/project/subscriptions/subscription") + event = createPubsubEvent(PubsubType.GOOGLE, "projects/project/subscriptions/subscription", []) pipeline = createPipelineWith(enabledGooglePubsubTrigger, disabledGooglePubsubTrigger) } @@ -113,17 +120,36 @@ class PubsubEventMonitorSpec extends Specification implements RetrofitStubs { 0 * subscriber._ where: - trigger | description - disabledGooglePubsubTrigger | "disabled Google pubsub trigger" - enabledGooglePubsubTrigger.withSubscriptionName("wrongName") | "different subscription name" - enabledGooglePubsubTrigger.withPubsubType("noogle") | "different subscription name" + trigger | description + disabledGooglePubsubTrigger | "disabled Google pubsub trigger" + enabledGooglePubsubTrigger.withSubscriptionName("wrongName") | "different subscription name" + enabledGooglePubsubTrigger.withPubsubType("noogle") | "different subscription name" pipeline = createPipelineWith(trigger) - event = createPubsubEvent(PubsubType.GOOGLE, "projects/project/subscriptions/subscription") + event = createPubsubEvent(PubsubType.GOOGLE, "projects/project/subscriptions/subscription", []) } @Unroll - def "does not trigger a pipeline that has an enabled bitbucket trigger with missing #field"() { + def "does not trigger #description pipelines containing artifacts for Google pubsub"() { + given: + pipelineCache.getPipelines() >> [pipeline] + + when: + monitor.processEvent(objectMapper.convertValue(event, Event)) + + then: + 0 * subscriber._ + + where: + trigger | description + enabledGooglePubsubTrigger.withExpectedArtifacts(badArtifacts) | "non-matching artifact in message" + + pipeline = createPipelineWith(trigger) + event = createPubsubEvent(PubsubType.GOOGLE, "projects/project/subscriptions/subscription", goodArtifacts) + } + + @Unroll + def "does not trigger a pipeline that has an enabled pubsub trigger with missing #field"() { given: pipelineCache.getPipelines() >> [badPipeline, goodPipeline] @@ -138,7 +164,7 @@ class PubsubEventMonitorSpec extends Specification implements RetrofitStubs { enabledGooglePubsubTrigger.withSubscriptionName(null) | "subscriptionName" enabledGooglePubsubTrigger.withPubsubType(null) | "pubsubType" - event = createPubsubEvent(PubsubType.GOOGLE, "projects/project/subscriptions/subscription") + event = createPubsubEvent(PubsubType.GOOGLE, "projects/project/subscriptions/subscription", []) goodPipeline = createPipelineWith(enabledGooglePubsubTrigger) badPipeline = createPipelineWith(trigger) } diff --git a/echo-pipelinetriggers/src/test/groovy/com/netflix/spinnaker/echo/test/RetrofitStubs.groovy b/echo-pipelinetriggers/src/test/groovy/com/netflix/spinnaker/echo/test/RetrofitStubs.groovy index a8232eccf..fae5e0bad 100644 --- a/echo-pipelinetriggers/src/test/groovy/com/netflix/spinnaker/echo/test/RetrofitStubs.groovy +++ b/echo-pipelinetriggers/src/test/groovy/com/netflix/spinnaker/echo/test/RetrofitStubs.groovy @@ -6,6 +6,7 @@ import com.netflix.spinnaker.echo.model.Trigger import com.netflix.spinnaker.echo.model.pubsub.MessageDescription import com.netflix.spinnaker.echo.model.pubsub.PubsubType import com.netflix.spinnaker.echo.model.trigger.* +import com.netflix.spinnaker.kork.artifacts.model.Artifact import retrofit.RetrofitError import retrofit.client.Response @@ -84,13 +85,14 @@ trait RetrofitStubs { return res } - PubsubEvent createPubsubEvent(PubsubType pubsubType, String subscriptionName) { + PubsubEvent createPubsubEvent(PubsubType pubsubType, String subscriptionName, List artifacts) { def res = new PubsubEvent() def description = MessageDescription.builder() .pubsubType(pubsubType) .ackDeadlineMillis(10000) .subscriptionName(subscriptionName) + .artifacts(artifacts) .build() def content = new PubsubEvent.Content()