From 9e0ae6abdd19a9c36ec6a4d990e7bb6d4ec9ebad Mon Sep 17 00:00:00 2001 From: Rob Fletcher Date: Tue, 24 Oct 2017 11:27:25 -0700 Subject: [PATCH] feat(dryrun) replace default notifications with custom --- .../spinnaker/echo/config/DryRunConfig.java | 33 +++++++++-- .../notification/DryRunNotificationAgent.java | 18 ++++-- .../DryRunNotificationAgentSpec.groovy | 59 ++++++++++++++++++- 3 files changed, 99 insertions(+), 11 deletions(-) diff --git a/echo-notifications/src/main/groovy/com/netflix/spinnaker/echo/config/DryRunConfig.java b/echo-notifications/src/main/groovy/com/netflix/spinnaker/echo/config/DryRunConfig.java index 9c5a8f8ec..13c1db8e2 100644 --- a/echo-notifications/src/main/groovy/com/netflix/spinnaker/echo/config/DryRunConfig.java +++ b/echo-notifications/src/main/groovy/com/netflix/spinnaker/echo/config/DryRunConfig.java @@ -16,13 +16,16 @@ package com.netflix.spinnaker.echo.config; +import java.util.List; import com.netflix.spinnaker.echo.notification.DryRunNotificationAgent; import com.netflix.spinnaker.echo.pipelinetriggers.orca.OrcaService; import com.netflix.spinnaker.echo.services.Front50Service; import com.squareup.okhttp.OkHttpClient; +import lombok.Data; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import retrofit.Endpoint; @@ -31,20 +34,22 @@ import static retrofit.Endpoints.newFixedEndpoint; @Configuration -@ConditionalOnProperty("dryRun.enabled") +@EnableConfigurationProperties(DryRunConfig.DryRunProperties.class) +@ConditionalOnProperty("dryrun.enabled") @Slf4j public class DryRunConfig { @Bean - Endpoint dryRunEndpoint(@Value("${dryRun.baseUrl}") String baseUrl) { - return newFixedEndpoint(baseUrl); + Endpoint dryRunEndpoint(DryRunProperties properties) { + return newFixedEndpoint(properties.getBaseUrl()); } @Bean DryRunNotificationAgent dryRunNotificationAgent( Front50Service front50, OkHttpClient okHttpClient, RestAdapter.LogLevel retrofitLogLevel, - Endpoint dryRunEndpoint) + Endpoint dryRunEndpoint, + DryRunProperties properties) { log.info("Pipeline dry runs will execute at {}", dryRunEndpoint.getUrl()); OrcaService orca = new RestAdapter.Builder() @@ -53,6 +58,22 @@ Endpoint dryRunEndpoint(@Value("${dryRun.baseUrl}") String baseUrl) { .setLogLevel(retrofitLogLevel) .build() .create(OrcaService.class); - return new DryRunNotificationAgent(front50, orca); + return new DryRunNotificationAgent(front50, orca, properties); + } + + @ConfigurationProperties("dryrun") + @Data + public static class DryRunProperties { + String baseUrl; + List notifications; + } + + // seems like I have to do this as Spring can't parse lists of strings from YAML + @Data + public static class Notification { + String type; + String address; + String level; + List when; } } diff --git a/echo-notifications/src/main/groovy/com/netflix/spinnaker/echo/notification/DryRunNotificationAgent.java b/echo-notifications/src/main/groovy/com/netflix/spinnaker/echo/notification/DryRunNotificationAgent.java index 579de5b8f..5154c3916 100644 --- a/echo-notifications/src/main/groovy/com/netflix/spinnaker/echo/notification/DryRunNotificationAgent.java +++ b/echo-notifications/src/main/groovy/com/netflix/spinnaker/echo/notification/DryRunNotificationAgent.java @@ -16,8 +16,11 @@ package com.netflix.spinnaker.echo.notification; +import java.util.List; import java.util.Map; import java.util.NoSuchElementException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.netflix.spinnaker.echo.config.DryRunConfig; import com.netflix.spinnaker.echo.model.Event; import com.netflix.spinnaker.echo.model.Trigger; import com.netflix.spinnaker.echo.pipelinetriggers.orca.OrcaService; @@ -30,10 +33,16 @@ public class DryRunNotificationAgent extends AbstractEventNotificationAgent { private final Front50Service front50; private final OrcaService orca; + private final DryRunConfig.DryRunProperties properties; - public DryRunNotificationAgent(Front50Service front50, OrcaService orca) { + public DryRunNotificationAgent( + Front50Service front50, + OrcaService orca, + DryRunConfig.DryRunProperties properties + ) { this.front50 = front50; this.orca = orca; + this.properties = properties; } @Override public String getNotificationType() { @@ -70,6 +79,7 @@ public void sendNotifications( .withName(format("%s (dry run)", pipeline.getName())) .withId(null) .withTrigger(trigger) + .withNotifications(mapper.convertValue(properties.getNotifications(), List.class)) ); }) .doOnError(ex -> { @@ -79,8 +89,8 @@ public void sendNotifications( log.error(format("Error triggering dry run of %s", pipelineConfigId), ex); } }) - .subscribe(response -> { - log.info("Pipeline triggered: {}", response); - }); + .subscribe(response -> log.info("Pipeline triggered: {}", response)); } + + private final ObjectMapper mapper = new ObjectMapper(); } diff --git a/echo-notifications/src/test/groovy/com/netflix/spinnaker/echo/notification/DryRunNotificationAgentSpec.groovy b/echo-notifications/src/test/groovy/com/netflix/spinnaker/echo/notification/DryRunNotificationAgentSpec.groovy index 2b4ca9ea9..e284f3c00 100644 --- a/echo-notifications/src/test/groovy/com/netflix/spinnaker/echo/notification/DryRunNotificationAgentSpec.groovy +++ b/echo-notifications/src/test/groovy/com/netflix/spinnaker/echo/notification/DryRunNotificationAgentSpec.groovy @@ -25,12 +25,14 @@ import spock.lang.Specification import spock.lang.Subject import spock.lang.Unroll import spock.util.concurrent.BlockingVariable +import static com.netflix.spinnaker.echo.config.DryRunConfig.DryRunProperties class DryRunNotificationAgentSpec extends Specification { def front50 = Mock(Front50Service) def orca = Mock(OrcaService) - @Subject def agent = new DryRunNotificationAgent(front50, orca) + def properties = new DryRunProperties() + @Subject def agent = new DryRunNotificationAgent(front50, orca, properties) @Unroll def "ignores #type:#status notifications"() { @@ -113,4 +115,59 @@ class DryRunNotificationAgentSpec extends Specification { ] ) } + + def "adds notifications to triggered pipeline"() { + given: + front50.getPipelines(application) >> Observable.just([pipeline]) + + and: + properties.notifications = [ + [ + type : "slack", + address: "#a-slack-channel", + level : "pipeline", + when : ["pipeline.failed"] + ] + ] + + and: + def captor = new BlockingVariable() + orca.trigger(_) >> { captor.set(it[0]) } + + when: + agent.processEvent(event) + + then: + with(captor.get()) { + notifications == properties.notifications + } + + where: + pipelineConfigId = "1" + application = "covfefe" + pipeline = new Pipeline.PipelineBuilder() + .application(application) + .name("a-pipeline") + .id(pipelineConfigId) + .build() + event = new Event( + details: [ + type : "orca:pipeline:complete", + application: application + ], + content: [ + execution: [ + name : pipeline.name, + notifications : [ + [ + type: "dryrun", + when: ["pipeline.complete"] + ] + ], + pipelineConfigId: pipelineConfigId, + status : "SUCCEEDED" + ] + ] + ) + } }