Skip to content

Commit

Permalink
Grpc Deployment proto endpoint (#841)
Browse files Browse the repository at this point in the history
* feat(grpc_deployment_endpoint): Add a grpc endpoint for deployment.
  • Loading branch information
brandonnelson3 committed Feb 14, 2018
1 parent d7e313c commit 8acf7aa
Show file tree
Hide file tree
Showing 15 changed files with 144 additions and 7 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -1,5 +1,6 @@
/.gradle/
build/
build-gen/
*.iml
*.ipr
*.iws
Expand Down
1 change: 1 addition & 0 deletions build.gradle
Expand Up @@ -26,6 +26,7 @@ buildscript {
dependencies {
classpath 'com.netflix.spinnaker.gradle:spinnaker-gradle-project:3.16.0'
classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.0'
}
}

Expand Down
4 changes: 2 additions & 2 deletions gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Thu Nov 26 11:25:11 PST 2015
#Mon Dec 11 14:43:59 EST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-3.5-all.zip
1 change: 1 addition & 0 deletions halyard-cli/halyard-cli.gradle
Expand Up @@ -4,6 +4,7 @@ dependencies {
compile project(':halyard-config')
compile project(':halyard-core')
compile project(':halyard-deploy')
compile project(':halyard-proto')
}

apply plugin: 'java'
Expand Down
Expand Up @@ -34,6 +34,7 @@
import org.yaml.snakeyaml.parser.ParserException;
import org.yaml.snakeyaml.scanner.ScannerException;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
Expand Down Expand Up @@ -97,6 +98,22 @@ Halconfig parseHalconfig(InputStream is) throws IllegalArgumentException {
}
}

/**
* Parse Halyard's config for inmemory usage. HalConfigs parsed with this function will NOT be written to disk for
* persistence.
*
* @param is is the input stream to read from.
* @return the fully parsed halconfig.
* @see Halconfig
*/
public Halconfig setInmemoryHalConfig(ByteArrayInputStream is) throws IllegalArgumentException {
Halconfig halconfig = parseHalconfig(is);

DaemonTaskHandler.setContext(halconfig);

return halconfig;
}

private InputStream getHalconfigStream() throws FileNotFoundException {
String path = useBackup ? backupHalconfigPath : halconfigPath;
return new FileInputStream(new File(path));
Expand Down
1 change: 1 addition & 0 deletions halyard-core/halyard-core.gradle
Expand Up @@ -7,4 +7,5 @@ dependencies {

compile 'com.beust:jcommander:1.48'
compile 'com.hubspot.jinjava:jinjava:2.2.3'
compile 'com.google.api.grpc:grpc-google-common-protos:1.0.5'
}
Expand Up @@ -99,6 +99,7 @@ public static class StaticRequestBuilder<K> {
private Supplier<K> buildResponse;
private Supplier<ProblemSet> validateResponse;
private Severity severity = Severity.WARNING;
private Runnable setup = () -> {};

public StaticRequestBuilder(Supplier<K> buildResponse) {
this.buildResponse = buildResponse;
Expand All @@ -112,6 +113,7 @@ public DaemonResponse<K> build() {
K responseBody;
ProblemSet problemSet = new ProblemSet();
try {
setup.run();
if (validateResponse != null) {
problemSet = validateResponse.get();
}
Expand Down
Expand Up @@ -3,6 +3,7 @@
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.longrunning.Operation;
import com.netflix.spinnaker.halyard.core.DaemonResponse;
import com.netflix.spinnaker.halyard.core.error.v1.HalException;
import com.netflix.spinnaker.halyard.core.problem.v1.Problem;
Expand Down Expand Up @@ -52,6 +53,11 @@ public DaemonTask(@JsonProperty("name") String name, @JsonProperty("timeout") lo
.getImplementationVersion()).orElse("Unknown");
}

public Operation getLRO() {
// TODO(brnelson): This shouldn't do this, it should set name to name and add the UUID as metadata.
return Operation.newBuilder().setName(this.uuid).setDone(this.state.isTerminal()).build();
}

void newStage(String name) {
currentStage = name;
}
Expand Down
Expand Up @@ -216,7 +216,8 @@ public RemoteAction prep(String deploymentName, List<String> serviceNames) {
return action;
}

public RemoteAction deploy(String deploymentName, List<DeployOption> deployOptions, List<String> serviceNames) {
public RemoteAction deploy(String deploymentName, List<DeployOption> deployOptions, List<String>
serviceNames) {
DeploymentConfiguration deploymentConfiguration = deploymentService.getDeploymentConfiguration(deploymentName);
SpinnakerServiceProvider<DeploymentDetails> serviceProvider = serviceProviderFactory.create(deploymentConfiguration);

Expand Down
47 changes: 47 additions & 0 deletions halyard-proto/halyard-proto.gradle
@@ -0,0 +1,47 @@
apply plugin: 'java'
apply plugin: 'com.google.protobuf'

dependencies {
compile 'io.grpc:grpc-all:1.8.0'
compile 'com.google.protobuf:protobuf-java:3.0.0'
compile 'com.google.guava:guava:23.5-jre'
compile 'com.google.api.grpc:grpc-google-common-protos:1.0.5'
}

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"
}
20 changes: 20 additions & 0 deletions halyard-proto/src/main/proto/deployments.proto
@@ -0,0 +1,20 @@
syntax = "proto3";

import "google/longrunning/operations.proto";

package spinnaker.halyard;

option java_package = "com.netflix.spinnaker.halyard.proto";
option java_multiple_files = true;

service Deployments {
// A Service for instructing Halyard to begin a deployment with the provided
// config.
rpc DeployConfig(DeployConfigRequest) returns (google.longrunning.Operation) {
}
}

message DeployConfigRequest {
string name = 1;
bytes config = 2;
}
7 changes: 6 additions & 1 deletion halyard-web/halyard-web.gradle
@@ -1,4 +1,5 @@
apply plugin: 'org.springframework.boot'
apply plugin: 'com.google.protobuf'
apply plugin: 'spinnaker.package'

ext {
Expand Down Expand Up @@ -30,7 +31,11 @@ dependencies {
compile project(':halyard-cli')
compile project(':halyard-config')
compile project(':halyard-core')
compile project(':halyard-deploy')
compile project(':halyard-deploy')
compile project(':halyard-proto')

compile('org.lognet:grpc-spring-boot-starter:2.1.4')
compile 'com.google.guava:guava:23.5-jre'
}

tasks.bootRepackage.enabled = project.repackage
Expand Down
Expand Up @@ -36,6 +36,8 @@
import com.netflix.spinnaker.halyard.deploy.services.v1.GenerateService;
import com.netflix.spinnaker.halyard.deploy.spinnaker.v1.RunningServiceDetails;
import com.netflix.spinnaker.halyard.deploy.spinnaker.v1.service.SpinnakerService;
import com.netflix.spinnaker.halyard.proto.DeploymentsGrpc;
import org.lognet.springboot.grpc.GRpcService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
Expand All @@ -44,6 +46,7 @@
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.ByteArrayInputStream;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
Expand All @@ -52,9 +55,10 @@
import java.util.function.Supplier;
import java.util.stream.Collectors;

@GRpcService
@RestController
@RequestMapping("/v1/config/deployments")
public class DeploymentController {
public class DeploymentController extends DeploymentsGrpc.DeploymentsImplBase{

@Autowired
DeploymentService deploymentService;
Expand Down Expand Up @@ -246,7 +250,8 @@ DaemonTask<Halconfig, RemoteAction> deploy(@PathVariable String deploymentName,
deployOptions != null ? deployOptions : Collections.emptyList();
List<String> finalServiceNames = serviceNames != null ? serviceNames : Collections.emptyList();
StaticRequestBuilder<RemoteAction> builder = new StaticRequestBuilder<>(
() -> deployService.deploy(deploymentName, finalDeployOptions, finalServiceNames));
() -> deployService.deploy(deploymentName, finalDeployOptions,
finalServiceNames));
builder.setSeverity(severity);

if (validate) {
Expand All @@ -257,6 +262,22 @@ DaemonTask<Halconfig, RemoteAction> deploy(@PathVariable String deploymentName,
.submitTask(builder::build, "Apply deployment", TimeUnit.MINUTES.toMillis(30));
}

public void deployConfig(com.netflix.spinnaker.halyard.proto.DeployConfigRequest request,
io.grpc.stub.StreamObserver<com.google.longrunning.Operation> responseObserver) {
StaticRequestBuilder<RemoteAction> builder = new StaticRequestBuilder<>(
() -> deployService.deploy(request.getName(), Collections.emptyList(),
Collections.emptyList()));
builder.setValidateResponse(() -> deploymentService.validateDeployment(request.getName()));
builder.setSeverity(Severity.WARNING);
builder.setSetup(() ->
halconfigParser.setInmemoryHalConfig(new ByteArrayInputStream(request.getConfig().toByteArray()))
);

responseObserver.onNext(DaemonTaskHandler
.submitTask(builder::build, "Apply deployment", TimeUnit.MINUTES.toMillis(30)).getLRO());
responseObserver.onCompleted();
}

@RequestMapping(value = "/{deploymentName:.+}/collectLogs/", method = RequestMethod.PUT)
DaemonTask<Halconfig, Void> collectLogs(@PathVariable String deploymentName,
@RequestParam(required = false, defaultValue = DefaultControllerValues.validate) boolean validate,
Expand Down
@@ -1,18 +1,25 @@
package com.netflix.spinnaker.halyard.controllers.v1;

import com.google.longrunning.GetOperationRequest;
import com.google.longrunning.Operation;
import com.google.longrunning.OperationsGrpc;
import com.google.longrunning.OperationsGrpc.OperationsImplBase;
import com.netflix.spinnaker.halyard.config.model.v1.node.Halconfig;
import com.netflix.spinnaker.halyard.core.DaemonResponse;
import com.netflix.spinnaker.halyard.core.tasks.v1.DaemonTask;
import com.netflix.spinnaker.halyard.core.tasks.v1.DaemonTaskHandler;
import com.netflix.spinnaker.halyard.core.tasks.v1.ShallowTaskList;
import com.netflix.spinnaker.halyard.core.tasks.v1.TaskRepository;
import com.netflix.spinnaker.halyard.proto.DeploymentsOuterClass;
import org.lognet.springboot.grpc.GRpcService;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import retrofit.http.Body;

@GRpcService
@RestController
@RequestMapping("/v1/tasks")
public class TaskController {
public class TaskController extends OperationsGrpc.OperationsImplBase {
@RequestMapping(value = "/{uuid:.+}/", method = RequestMethod.GET)
DaemonTask<Halconfig, Void> getTask(@PathVariable String uuid) {
return TaskRepository.getTask(uuid);
Expand Down Expand Up @@ -40,4 +47,10 @@ public TaskNotFoundException(String msg) {
super(msg);
}
}

@Override
public void getOperation(GetOperationRequest request, io.grpc.stub.StreamObserver<com.google.longrunning.Operation> responseObserver) {
responseObserver.onNext(TaskRepository.getTask(request.getName()).getLRO());
responseObserver.onCompleted();
}
}
1 change: 1 addition & 0 deletions settings.gradle
Expand Up @@ -21,6 +21,7 @@ include 'halyard-cli'
include 'halyard-config'
include 'halyard-core'
include 'halyard-deploy'
include 'halyard-proto'
include 'halyard-web'

def setBuildFile(project) {
Expand Down

0 comments on commit 8acf7aa

Please sign in to comment.