From 5e46d0401310799bf6bb71930e117c5f17baa9d5 Mon Sep 17 00:00:00 2001 From: Travis Tomsu Date: Tue, 3 Sep 2019 11:09:36 -0400 Subject: [PATCH] feat(telemetry): Use kork-proto as source of proto library, remove some GRPC cruft. --- build.gradle | 1 - echo-proto/echo-proto.gradle | 47 ----- echo-proto/src/main/proto/event.proto | 83 --------- echo-telemetry/echo-telemetry.gradle | 7 +- .../echo/config/TelemetryConfig.java | 55 +++--- .../telemetry/TelemetryEventListener.java | 152 ++++++++++++---- .../echo/telemetry/TelemetryService.java | 5 +- .../echo/TelemetryEventListenerSpec.groovy | 54 ------ .../TelemetryEventListenerSpec.groovy | 162 ++++++++++++++++++ echo-web/echo-web.gradle | 1 - lombok.config | 1 + settings.gradle | 5 +- 12 files changed, 322 insertions(+), 251 deletions(-) delete mode 100644 echo-proto/echo-proto.gradle delete mode 100644 echo-proto/src/main/proto/event.proto delete mode 100644 echo-telemetry/src/test/groovy/com/netflix/spinnaker/echo/TelemetryEventListenerSpec.groovy create mode 100644 echo-telemetry/src/test/groovy/com/netflix/spinnaker/echo/telemetry/TelemetryEventListenerSpec.groovy create mode 100644 lombok.config diff --git a/build.gradle b/build.gradle index 6de8830aa..332062675 100644 --- a/build.gradle +++ b/build.gradle @@ -28,7 +28,6 @@ buildscript { // this override is needed to omit compileOnly dependencies from generated pom.xml classpath "com.netflix.nebula:nebula-publishing-plugin:12.0.1" } - classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.8' } } diff --git a/echo-proto/echo-proto.gradle b/echo-proto/echo-proto.gradle deleted file mode 100644 index 3ae7e3cad..000000000 --- a/echo-proto/echo-proto.gradle +++ /dev/null @@ -1,47 +0,0 @@ -apply plugin: 'java' -apply plugin: 'com.google.protobuf' - -dependencies { - implementation 'com.google.protobuf:protobuf-java:3.0.0' - implementation 'com.google.guava:guava:23.5-jre' - api 'com.google.api.grpc:grpc-google-common-protos:1.0.5' - implementation 'io.grpc:grpc-all:1.8.0' -} - -protobuf { - protoc { - artifact = 'com.google.protobuf:protoc:3.5.1-1' - } - plugins { - grpc { - artifact = "io.grpc:protoc-gen-grpc-java:1.9.0" - } - } - - generateProtoTasks { - ofSourceSet('main').each { task -> - task.builtins { - java{ - outputSubDir = 'java' - } - } - task.plugins { - grpc { - outputSubDir = 'java' - } - } - task.descriptorSetOptions.includeImports = true - } - } - generatedFilesBaseDir = "${projectDir}/build-gen/proto" -} - -idea { - module { - sourceDirs += file("${projectDir}/build-gen/proto/main/java") - } -} - -clean { - delete "${projectDir}/build-gen" -} diff --git a/echo-proto/src/main/proto/event.proto b/echo-proto/src/main/proto/event.proto deleted file mode 100644 index 274ec6b48..000000000 --- a/echo-proto/src/main/proto/event.proto +++ /dev/null @@ -1,83 +0,0 @@ -syntax = "proto3"; - -package spinnaker.echo; - -option java_package = "com.netflix.spinnaker.echo.proto"; -option java_multiple_files = true; - -message EventProto { - SpinnakerInstance spinnaker_instance = 1; -} - -message SpinnakerInstance { - string id = 1; - string version = 2; - Application application = 3; -} - -message Application { - string id = 1; - Execution execution = 2; -} - -message Execution { - string id = 1; - Type type = 2; - - enum Type { - UNKNOWN = 0; - PIPELINE = 1; - ORCHESTRATION = 2; - MANAGED_PIPELINE_TEMPLATE_V1 = 3; - MANAGED_PIPELINE_TEMPLATE_V2 = 4; - } - - Trigger trigger = 3; - - message Trigger { - Type type = 1; - - // Sourced from https://github.com/spinnaker/echo/tree/master/echo-model/src/main/java/com/netflix/spinnaker/echo/model/trigger - enum Type { - UNKNOWN = 0; - ARTIFACTORY = 1; - BUILD = 2; - DOCKER = 3; - GIT = 4; - MANUAL = 5; - PUBSUB = 6; - WEBHOOK = 7; - } - } - - repeated Stage stages = 4; - Status status = 5; -} - -message Stage { - string type = 1; - Status status = 2; - CloudProvider cloud_provider = 3; -} - -message CloudProvider { - string id = 1; - string variant = 2; -} - -// Sourced from https://github.com/spinnaker/orca/blob/master/orca-core/src/main/java/com/netflix/spinnaker/orca/ExecutionStatus.java -enum Status { - UNKNOWN = 0; - NOT_STARTED = 1; - RUNNING = 2; - PAUSED = 3; - SUSPENDED = 4; - SUCCEEDED = 5; - FAILED_CONTINUE = 6; - TERMINAL = 7; - CANCELED = 8; - REDIRECT = 9; - STOPPED = 10; - SKIPPED = 11; - BUFFERED = 12; -} \ No newline at end of file diff --git a/echo-telemetry/echo-telemetry.gradle b/echo-telemetry/echo-telemetry.gradle index bbec71baf..988851e22 100644 --- a/echo-telemetry/echo-telemetry.gradle +++ b/echo-telemetry/echo-telemetry.gradle @@ -17,9 +17,10 @@ dependencies { implementation project(':echo-model') implementation project(':echo-notifications') - implementation project(':echo-proto') + implementation 'com.google.guava:guava' + implementation 'com.google.protobuf:protobuf-java-util' + implementation "com.netflix.spinnaker.kork:kork-proto" + implementation 'com.netflix.spinnaker.kork:kork-web' implementation 'com.squareup.retrofit:retrofit' implementation 'com.squareup.retrofit:converter-jackson' - implementation 'com.netflix.spinnaker.kork:kork-web' - implementation 'com.google.protobuf:protobuf-java-util:3.7.1' } diff --git a/echo-telemetry/src/main/java/com/netflix/spinnaker/echo/config/TelemetryConfig.java b/echo-telemetry/src/main/java/com/netflix/spinnaker/echo/config/TelemetryConfig.java index 07dceaa1e..8cc742cdd 100644 --- a/echo-telemetry/src/main/java/com/netflix/spinnaker/echo/config/TelemetryConfig.java +++ b/echo-telemetry/src/main/java/com/netflix/spinnaker/echo/config/TelemetryConfig.java @@ -16,50 +16,65 @@ package com.netflix.spinnaker.echo.config; -import static retrofit.Endpoints.newFixedEndpoint; - import com.netflix.spinnaker.echo.telemetry.TelemetryService; +import com.netflix.spinnaker.retrofit.RetrofitConfigurationProperties; import com.netflix.spinnaker.retrofit.Slf4jRetrofitLogger; -import groovy.transform.CompileStatic; +import com.squareup.okhttp.OkHttpClient; +import java.util.concurrent.TimeUnit; +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; import retrofit.RestAdapter; -import retrofit.client.Client; +import retrofit.client.OkClient; import retrofit.converter.JacksonConverter; @Slf4j @Configuration @ConditionalOnProperty("telemetry.enabled") -@CompileStatic -class TelemetryConfig { - - @Value("${telemetry.endpoint}") - String endpoint; - - @Bean - Endpoint telemetryEndpoint() { - return newFixedEndpoint(endpoint); - } +@EnableConfigurationProperties(TelemetryConfig.TelemetryConfigProps.class) +public class TelemetryConfig { @Bean public TelemetryService telemetryService( - Endpoint telemetryEndpoint, Client retrofitClient, RestAdapter.LogLevel retrofitLogLevel) { + RetrofitConfigurationProperties retrofitConfigurationProperties, + TelemetryConfigProps configProps) { log.info("Telemetry service loaded"); TelemetryService client = new RestAdapter.Builder() - .setEndpoint(telemetryEndpoint) + .setEndpoint(configProps.endpoint) .setConverter(new JacksonConverter()) - .setClient(retrofitClient) - .setLogLevel(RestAdapter.LogLevel.FULL) + .setClient(telemetryOkClient(configProps)) + .setLogLevel(retrofitConfigurationProperties.getLogLevel()) .setLog(new Slf4jRetrofitLogger(TelemetryService.class)) .build() .create(TelemetryService.class); return client; } + + private OkClient telemetryOkClient(TelemetryConfigProps configProps) { + OkHttpClient httpClient = new OkHttpClient(); + httpClient.setConnectTimeout(configProps.connectionTimeoutMillis, TimeUnit.MILLISECONDS); + httpClient.setReadTimeout(configProps.readTimeoutMillis, TimeUnit.MILLISECONDS); + return new OkClient(httpClient); + } + + @Data + @ConfigurationProperties(prefix = "telemetry") + public static class TelemetryConfigProps { + + public static final String DEFAULT_TELEMETRY_ENDPOINT = "https://stats.spinnaker.io/log"; + + boolean enabled = false; + String endpoint = DEFAULT_TELEMETRY_ENDPOINT; + String instanceId; + String spinnakerVersion = "unknown"; + int connectionTimeoutMillis = 3000; + int readTimeoutMillis = 5000; + } } diff --git a/echo-telemetry/src/main/java/com/netflix/spinnaker/echo/telemetry/TelemetryEventListener.java b/echo-telemetry/src/main/java/com/netflix/spinnaker/echo/telemetry/TelemetryEventListener.java index db50d26be..56d94810a 100644 --- a/echo-telemetry/src/main/java/com/netflix/spinnaker/echo/telemetry/TelemetryEventListener.java +++ b/echo-telemetry/src/main/java/com/netflix/spinnaker/echo/telemetry/TelemetryEventListener.java @@ -16,82 +16,160 @@ package com.netflix.spinnaker.echo.telemetry; +import com.google.common.collect.ImmutableSet; +import com.google.common.hash.Hashing; import com.google.protobuf.util.JsonFormat; +import com.netflix.spinnaker.echo.config.TelemetryConfig; import com.netflix.spinnaker.echo.events.EchoEventListener; import com.netflix.spinnaker.echo.model.Event; -import com.netflix.spinnaker.echo.proto.*; +import com.netflix.spinnaker.kork.proto.stats.Application; +import com.netflix.spinnaker.kork.proto.stats.Execution; +import com.netflix.spinnaker.kork.proto.stats.SpinnakerInstance; +import com.netflix.spinnaker.kork.proto.stats.Stage; +import com.netflix.spinnaker.kork.proto.stats.Status; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; +import retrofit.mime.TypedString; @Slf4j @Component @ConditionalOnProperty("telemetry.enabled") public class TelemetryEventListener implements EchoEventListener { + + private static final Set LOGGABLE_DETAIL_TYPES = + ImmutableSet.of( + "orca:orchestration:complete", + "orca:orchestration:failed", + "orca:pipeline:complete", + "orca:pipeline:failed"); + + private static final JsonFormat.Printer JSON_PRINTER = + JsonFormat.printer().omittingInsignificantWhitespace(); + private final TelemetryService telemetryService; + private final TelemetryConfig.TelemetryConfigProps telemetryConfigProps; + @Autowired - public TelemetryEventListener(TelemetryService telemetryService) { + public TelemetryEventListener( + TelemetryService telemetryService, + TelemetryConfig.TelemetryConfigProps telemetryConfigProps) { this.telemetryService = telemetryService; + this.telemetryConfigProps = telemetryConfigProps; } - @Value("${telemetry.instanceId}") - String instanceId; - + @SuppressWarnings("unchecked") @Override public void processEvent(Event event) { try { - if (event.getDetails() == null - || !event.getDetails().getType().equals("orca:pipeline:complete")) { + if (event.getDetails() == null || event.getContent() == null) { + log.debug("Telemetry not sent: Details or content not found in event"); + return; + } + + String eventType = event.getDetails().getType(); + if (!LOGGABLE_DETAIL_TYPES.contains(eventType)) { + log.debug("Telemetry not sent: type '{}' not whitelisted ", eventType); return; } - Map execution = (Map) event.content.get("execution"); - Execution.Builder executionBuilder = getExecutionBuilder(execution); + String applicationId = event.getDetails().getApplication(); + if (applicationId == null || applicationId.isEmpty()) { + log.debug("Application ID must be non-null and not empty"); + return; + } - List stages = (ArrayList) execution.get("stages"); - for (Map stage : stages) { - executionBuilder.addStages(getStageBuilder(stage)); + Map execution = (Map) event.getContent().get("execution"); + if (execution == null || execution.isEmpty()) { + log.debug("Missing execution from Event content."); + return; } - Application.Builder applicationBuilder = - Application.newBuilder() - .setId(event.details.getApplication()) - .setExecution(executionBuilder); + String hashedApplicationId = hash(applicationId); + Execution.Type executionType = + Execution.Type.valueOf( + // TODO(ttomsu, louisjimenez): Add MPTv1 and v2 execution type detection. + execution.getOrDefault("type", "UNKNOWN").toString().toUpperCase()); + Status executionStatus = + Status.valueOf(execution.getOrDefault("status", "UNKNOWN").toString().toUpperCase()); + + Map trigger = + (Map) execution.getOrDefault("trigger", new HashMap<>()); + Execution.Trigger.Type triggerType = + Execution.Trigger.Type.valueOf( + trigger.getOrDefault("type", "UNKNOWN").toString().toUpperCase()); - SpinnakerInstance.Builder spinnakerInstance = - SpinnakerInstance.newBuilder().setId(instanceId).setApplication(applicationBuilder); + List stages = (List) execution.getOrDefault("stages", new ArrayList<>()); + List protoStages = stages.stream().map(this::toStage).collect(Collectors.toList()); - EventProto.Builder eventProto = - EventProto.newBuilder().setSpinnakerInstance(spinnakerInstance); + Execution.Builder executionBuilder = + Execution.newBuilder() + .setType(executionType) + .setStatus(executionStatus) + .setTrigger(Execution.Trigger.newBuilder().setType(triggerType)) + .addAllStages(protoStages); + String executionId = execution.getOrDefault("id", "").toString(); + if (!executionId.isEmpty()) { + executionBuilder.setId(hash(executionId)); + } + Execution executionProto = executionBuilder.build(); + + Application application = Application.newBuilder().setId(hashedApplicationId).build(); + + SpinnakerInstance spinnakerInstance = + SpinnakerInstance.newBuilder() + .setId(telemetryConfigProps.getInstanceId()) + .setVersion(telemetryConfigProps.getSpinnakerVersion()) + .build(); - telemetryService.sendMessage(JsonFormat.printer().print(eventProto)); + com.netflix.spinnaker.kork.proto.stats.Event loggedEvent = + com.netflix.spinnaker.kork.proto.stats.Event.newBuilder() + .setSpinnakerInstance(spinnakerInstance) + .setApplication(application) + .setExecution(executionProto) + .build(); + String content = JSON_PRINTER.print(loggedEvent); + telemetryService.log(new TypedJsonString(content)); + log.debug("Telemetry sent!"); } catch (Exception e) { - log.error("Could not send Telemetry event {}", event, e); + log.warn("Could not send Telemetry event {}", event, e); } } - private Execution.Builder getExecutionBuilder(Map execution) { - Map trigger = (Map) execution.get("trigger"); - return Execution.newBuilder() - .setId(execution.get("id").toString()) - .setType(Execution.Type.valueOf(execution.get("type").toString().toUpperCase())) - .setTrigger( - Execution.Trigger.newBuilder() - .setType( - Execution.Trigger.Type.valueOf(trigger.get("type").toString().toUpperCase()))) - .setStatus(Status.valueOf(execution.get("status").toString().toUpperCase())); + private Stage toStage(Map stage) { + return Stage.newBuilder() + .setType(stage.getOrDefault("type", "unknown").toString()) + .setStatus(Status.valueOf(stage.getOrDefault("status", "UNKNOWN").toString().toUpperCase())) + .build(); } - private Stage.Builder getStageBuilder(Map stage) { - return Stage.newBuilder() - .setType(stage.get("type").toString()) - .setStatus(Status.valueOf(stage.get("status").toString().toUpperCase())); + private String hash(String clearText) { + return Hashing.sha256().hashString(clearText, StandardCharsets.UTF_8).toString(); + } + + static class TypedJsonString extends TypedString { + TypedJsonString(String body) { + super(body); + } + + @Override + public String mimeType() { + return "application/json"; + } + + @Override + public String toString() { + return new String(getBytes(), StandardCharsets.UTF_8); + } } } diff --git a/echo-telemetry/src/main/java/com/netflix/spinnaker/echo/telemetry/TelemetryService.java b/echo-telemetry/src/main/java/com/netflix/spinnaker/echo/telemetry/TelemetryService.java index f5bf25d2c..6339aff2e 100644 --- a/echo-telemetry/src/main/java/com/netflix/spinnaker/echo/telemetry/TelemetryService.java +++ b/echo-telemetry/src/main/java/com/netflix/spinnaker/echo/telemetry/TelemetryService.java @@ -19,8 +19,9 @@ import retrofit.client.Response; import retrofit.http.Body; import retrofit.http.POST; +import retrofit.mime.TypedInput; public interface TelemetryService { - @POST("/}") - Response sendMessage(@Body String body); + @POST("/log") + Response log(@Body TypedInput body); } diff --git a/echo-telemetry/src/test/groovy/com/netflix/spinnaker/echo/TelemetryEventListenerSpec.groovy b/echo-telemetry/src/test/groovy/com/netflix/spinnaker/echo/TelemetryEventListenerSpec.groovy deleted file mode 100644 index 74aafd1d9..000000000 --- a/echo-telemetry/src/test/groovy/com/netflix/spinnaker/echo/TelemetryEventListenerSpec.groovy +++ /dev/null @@ -1,54 +0,0 @@ -package com.netflix.spinnaker.echo - -import com.netflix.spinnaker.echo.model.Event -import com.netflix.spinnaker.echo.telemetry.TelemetryEventListener -import com.netflix.spinnaker.echo.telemetry.TelemetryService -import spock.lang.Specification -import spock.lang.Subject - -class TelemetryEventListenerSpec extends Specification { - def service = Mock(TelemetryService) - - @Subject - def listener = new TelemetryEventListener(service) - - void setup() { - listener.instanceId = "test-instance" - } - - def "send a telemetry event"() { - given: - Event event = new Event( - details: [ - type : "orca:pipeline:complete", - application: "some-application" - ], - content: [ - execution: [ - id : "execution_id", - type : "PIPELINE", - status : "SUCCEEDED", - trigger: [ - type: "GIT" - ], - stages : [ - [ - type : "deploy", - status: "SUCCEEDED" - ], - [ - type : "wait", - status: "SUCCEEDED" - ], - ] - ] - ] - ) - - when: - listener.processEvent(event) - - then: - 1 * service.sendMessage('{\n "spinnakerInstance": {\n "id": "test-instance",\n "application": {\n "id": "some-application",\n "execution": {\n "id": "execution_id",\n "type": "PIPELINE",\n "trigger": {\n "type": "GIT"\n },\n "stages": [{\n "type": "deploy",\n "status": "SUCCEEDED"\n }, {\n "type": "wait",\n "status": "SUCCEEDED"\n }],\n "status": "SUCCEEDED"\n }\n }\n }\n}') - } -} diff --git a/echo-telemetry/src/test/groovy/com/netflix/spinnaker/echo/telemetry/TelemetryEventListenerSpec.groovy b/echo-telemetry/src/test/groovy/com/netflix/spinnaker/echo/telemetry/TelemetryEventListenerSpec.groovy new file mode 100644 index 000000000..89771b4b1 --- /dev/null +++ b/echo-telemetry/src/test/groovy/com/netflix/spinnaker/echo/telemetry/TelemetryEventListenerSpec.groovy @@ -0,0 +1,162 @@ +package com.netflix.spinnaker.echo.telemetry + +import com.google.protobuf.util.JsonFormat +import com.netflix.spinnaker.echo.config.TelemetryConfig +import com.netflix.spinnaker.echo.model.Event +import com.netflix.spinnaker.kork.proto.stats.* +import com.netflix.spinnaker.kork.proto.stats.Event as EventProto +import retrofit.client.Response +import spock.lang.Specification +import spock.lang.Subject + +class TelemetryEventListenerSpec extends Specification { + + def service = Mock(TelemetryService) + + def instanceId = "test-instance" + def spinnakerVersion = "1.2.3" + + def applicationName = "someApp" + def applicationHash = "e40464bf6d04933c6011c29974eb328777669813a76583e5d547941427df686f" + + def executionId = "execution_id" + def executionHash = "6d6de5b8d67c11fff6d817ea3e1190bc63857de0329d253b21aef6e5c6bbebf9" + + def "test Event validation"() { + given: + def configProps = new TelemetryConfig.TelemetryConfigProps() + + @Subject + def listener = new TelemetryEventListener(service, configProps) + + when: "null details" + listener.processEvent(new Event()) + + then: + 0 * service.log(_) + + when: "null content" + listener.processEvent(new Event( + details: [:] + )) + + then: + 0 * service.log(_) + + when: "irrelevant details type are ignored" + listener.processEvent(new Event( + details: [ + type: "foobar1", + ], + content: [:] + )) + + then: + 0 * service.log(_) + + when: "missing application ID" + listener.processEvent(new Event( + details: [ + type: "orca:orchestration:complete", + ], + content: [:] + )) + + then: + 0 * service.log(_) + + when: "no execution in content" + listener.processEvent(new Event( + details: [ + type: "orca:orchestration:complete", + application: "foobar", + ], + content: [ + execution: [:], + ], + )) + + then: + 0 * service.log(_) + } + + def "send a telemetry event"() { + given: + def configProps = new TelemetryConfig.TelemetryConfigProps() + .setInstanceId(instanceId) + .setSpinnakerVersion(spinnakerVersion) + + @Subject + def listener = new TelemetryEventListener(service, configProps) + + when: + listener.processEvent(new Event( + details: [ + type : "orca:pipeline:complete", + application: applicationName, + ], + content: [ + execution: [ + id : executionId, + type : "PIPELINE", + status : "SUCCEEDED", + trigger: [ + type: "GIT" + ], + stages : [ + [ + type : "deploy", + status: "SUCCEEDED" + ], + [ + type : "wait", + status: "TERMINAL" + ], + ] + ] + ] + )) + + then: + 1 * service.log(_) >> { List args -> + String body = args[0]?.toString() + assert body != null + + // Note the handy Groovy feature of import aliasing Event->EventProto + EventProto.Builder eventBuilder = EventProto.newBuilder() + JsonFormat.parser().merge(body, eventBuilder) + + EventProto e = eventBuilder.build() + assert e != null + + SpinnakerInstance s = e.spinnakerInstance + assert s != null + assert s.id == instanceId + assert s.version == spinnakerVersion + + Application a = e.getApplication() + assert a != null + assert a.id == applicationHash + + Execution ex = e.getExecution() + assert ex != null + assert ex.id == executionHash + assert ex.type == Execution.Type.PIPELINE + assert ex.trigger.type == Execution.Trigger.Type.GIT + + List stages = ex.getStagesList() + assert stages != null + assert stages.size() == 2 + + Stage stage1 = stages.get(0) + assert stage1.type == "deploy" + assert stage1.status == Status.SUCCEEDED + + Stage stage2 = stages.get(1) + assert stage2.type == "wait" + assert stage2.status == Status.TERMINAL + + return new Response("url", 200, "", [], null) + } + } +} diff --git a/echo-web/echo-web.gradle b/echo-web/echo-web.gradle index 04b00e880..cf4e01b3e 100644 --- a/echo-web/echo-web.gradle +++ b/echo-web/echo-web.gradle @@ -35,7 +35,6 @@ dependencies { implementation project(':echo-pubsub-core') implementation project(':echo-pubsub-aws') implementation project(':echo-pubsub-google') - implementation project(':echo-proto') implementation project(':echo-telemetry') implementation "com.netflix.spinnaker.fiat:fiat-api:$fiatVersion" implementation "com.netflix.spinnaker.fiat:fiat-core:$fiatVersion" diff --git a/lombok.config b/lombok.config new file mode 100644 index 000000000..8aaaa7de5 --- /dev/null +++ b/lombok.config @@ -0,0 +1 @@ +lombok.accessors.chain = true diff --git a/settings.gradle b/settings.gradle index 9687449d7..99426f600 100644 --- a/settings.gradle +++ b/settings.gradle @@ -15,6 +15,7 @@ */ include 'echo-artifacts', + 'echo-bom', 'echo-core', 'echo-model', 'echo-web', @@ -26,10 +27,8 @@ include 'echo-artifacts', 'echo-pubsub-core', 'echo-pubsub-aws', 'echo-pubsub-google', - 'echo-test', - 'echo-bom', 'echo-telemetry', - 'echo-proto' + 'echo-test', rootProject.name = 'echo'