Skip to content

Commit

Permalink
feat(telemetry): adds listener for telemetry metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
staciegravy authored and Travis Tomsu committed Sep 11, 2019
1 parent 84dd5ba commit 16b5ab4
Show file tree
Hide file tree
Showing 10 changed files with 403 additions and 1 deletion.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ 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'
}
}

Expand Down
47 changes: 47 additions & 0 deletions echo-proto/echo-proto.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
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"
}
83 changes: 83 additions & 0 deletions echo-proto/src/main/proto/event.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
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;
}
25 changes: 25 additions & 0 deletions echo-telemetry/echo-telemetry.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2019 Armory, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

dependencies {
implementation project(':echo-model')
implementation project(':echo-notifications')
implementation project(':echo-proto')
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'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright 2019 Armory, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.netflix.spinnaker.echo.config;

import static retrofit.Endpoints.newFixedEndpoint;

import com.netflix.spinnaker.echo.telemetry.TelemetryService;
import com.netflix.spinnaker.retrofit.Slf4jRetrofitLogger;
import groovy.transform.CompileStatic;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import retrofit.Endpoint;
import retrofit.RestAdapter;
import retrofit.client.Client;
import retrofit.converter.JacksonConverter;

@Slf4j
@Configuration
@ConditionalOnProperty("telemetry.enabled")
@CompileStatic
class TelemetryConfig {

@Value("${telemetry.endpoint}")
String endpoint;

@Bean
Endpoint telemetryEndpoint() {
return newFixedEndpoint(endpoint);
}

@Bean
public TelemetryService telemetryService(
Endpoint telemetryEndpoint, Client retrofitClient, RestAdapter.LogLevel retrofitLogLevel) {
log.info("Telemetry service loaded");

TelemetryService client =
new RestAdapter.Builder()
.setEndpoint(telemetryEndpoint)
.setConverter(new JacksonConverter())
.setClient(retrofitClient)
.setLogLevel(RestAdapter.LogLevel.FULL)
.setLog(new Slf4jRetrofitLogger(TelemetryService.class))
.build()
.create(TelemetryService.class);

return client;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright 2019 Armory, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.netflix.spinnaker.echo.telemetry;

import com.google.protobuf.util.JsonFormat;
import com.netflix.spinnaker.echo.events.EchoEventListener;
import com.netflix.spinnaker.echo.model.Event;
import com.netflix.spinnaker.echo.proto.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
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;

@Slf4j
@Component
@ConditionalOnProperty("telemetry.enabled")
public class TelemetryEventListener implements EchoEventListener {
private final TelemetryService telemetryService;

@Autowired
public TelemetryEventListener(TelemetryService telemetryService) {
this.telemetryService = telemetryService;
}

@Value("${telemetry.instanceId}")
String instanceId;

@Override
public void processEvent(Event event) {
try {
if (event.getDetails() == null
|| !event.getDetails().getType().equals("orca:pipeline:complete")) {
return;
}

Map execution = (Map) event.content.get("execution");
Execution.Builder executionBuilder = getExecutionBuilder(execution);

List<Map> stages = (ArrayList<Map>) execution.get("stages");
for (Map stage : stages) {
executionBuilder.addStages(getStageBuilder(stage));
}

Application.Builder applicationBuilder =
Application.newBuilder()
.setId(event.details.getApplication())
.setExecution(executionBuilder);

SpinnakerInstance.Builder spinnakerInstance =
SpinnakerInstance.newBuilder().setId(instanceId).setApplication(applicationBuilder);

EventProto.Builder eventProto =
EventProto.newBuilder().setSpinnakerInstance(spinnakerInstance);

telemetryService.sendMessage(JsonFormat.printer().print(eventProto));

} catch (Exception e) {
log.error("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.Builder getStageBuilder(Map stage) {
return Stage.newBuilder()
.setType(stage.get("type").toString())
.setStatus(Status.valueOf(stage.get("status").toString().toUpperCase()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2019 Armory, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.netflix.spinnaker.echo.telemetry;

import retrofit.client.Response;
import retrofit.http.Body;
import retrofit.http.POST;

public interface TelemetryService {
@POST("/}")
Response sendMessage(@Body String body);
}
Loading

0 comments on commit 16b5ab4

Please sign in to comment.