From ec360a4f007321487ff5ef6a2e0d5992e7fd72aa Mon Sep 17 00:00:00 2001 From: Eugene Bekker Date: Wed, 6 Mar 2019 17:01:34 -0500 Subject: [PATCH] adding go-plugin example using C# --- examples/grpc/plugin-dotnet/.gitignore | 3 + examples/grpc/plugin-dotnet/HealthService.cs | 29 + examples/grpc/plugin-dotnet/Plugin.cs | 64 ++ examples/grpc/plugin-dotnet/Proto/Health.cs | 321 ++++++++++ .../grpc/plugin-dotnet/Proto/HealthGrpc.cs | 111 ++++ .../plugin-dotnet/Proto/HealthServiceImpl.cs | 113 ++++ examples/grpc/plugin-dotnet/Proto/Kv.cs | 565 ++++++++++++++++++ examples/grpc/plugin-dotnet/Proto/KvGrpc.cs | 138 +++++ .../grpc/plugin-dotnet/Proto/health.proto | 24 + examples/grpc/plugin-dotnet/README.md | 22 + .../grpc/plugin-dotnet/plugin-dotnet.csproj | 26 + 11 files changed, 1416 insertions(+) create mode 100644 examples/grpc/plugin-dotnet/.gitignore create mode 100644 examples/grpc/plugin-dotnet/HealthService.cs create mode 100644 examples/grpc/plugin-dotnet/Plugin.cs create mode 100644 examples/grpc/plugin-dotnet/Proto/Health.cs create mode 100644 examples/grpc/plugin-dotnet/Proto/HealthGrpc.cs create mode 100644 examples/grpc/plugin-dotnet/Proto/HealthServiceImpl.cs create mode 100644 examples/grpc/plugin-dotnet/Proto/Kv.cs create mode 100644 examples/grpc/plugin-dotnet/Proto/KvGrpc.cs create mode 100644 examples/grpc/plugin-dotnet/Proto/health.proto create mode 100644 examples/grpc/plugin-dotnet/README.md create mode 100644 examples/grpc/plugin-dotnet/plugin-dotnet.csproj diff --git a/examples/grpc/plugin-dotnet/.gitignore b/examples/grpc/plugin-dotnet/.gitignore new file mode 100644 index 00000000..06dfd059 --- /dev/null +++ b/examples/grpc/plugin-dotnet/.gitignore @@ -0,0 +1,3 @@ +bin +obj +go-plugins diff --git a/examples/grpc/plugin-dotnet/HealthService.cs b/examples/grpc/plugin-dotnet/HealthService.cs new file mode 100644 index 00000000..6cc011e5 --- /dev/null +++ b/examples/grpc/plugin-dotnet/HealthService.cs @@ -0,0 +1,29 @@ +using Grpc.Core; + +namespace plugin_dotnet +{ + public static class HealthService + { + public static IHealthService Get() => + new Grpc.HealthCheck.HealthServiceImpl(); + + public static ServerServiceDefinition BindService(IHealthService health) => + Grpc.Health.V1.Health.BindService((Grpc.Health.V1.Health.HealthBase)health); + } + + public interface IHealthService + { + void ClearAll(); + void ClearStatus(string service); + void SetStatus(string service, HealthStatus status); + + + } + + public enum HealthStatus + { + Unknown = 0, + Serving = 1, + NotServing = 2, + } +} \ No newline at end of file diff --git a/examples/grpc/plugin-dotnet/Plugin.cs b/examples/grpc/plugin-dotnet/Plugin.cs new file mode 100644 index 00000000..54a26ab9 --- /dev/null +++ b/examples/grpc/plugin-dotnet/Plugin.cs @@ -0,0 +1,64 @@ + +using System; +using System.IO; +using System.Threading.Tasks; +using Google.Protobuf; +using Grpc.Core; +using Proto; + +namespace plugin_dotnet +{ + class Plugin : KV.KVBase + { + public const string ServiceHost = "localhost"; + public const int ServicePort = 1234; + public const int AppProtoVersion = 1; + + public override async Task Put(PutRequest request, ServerCallContext context) + { + var filename = $"kv_{request.Key}"; + await File.WriteAllTextAsync(filename, + $"{request.Value.ToStringUtf8()}\n\nWritten from plugin-dotnet\n"); + + return new Empty(); + } + + public override async Task Get(GetRequest request, ServerCallContext context) + { + var filename = $"kv_{request.Key}"; + return new GetResponse + { + Value = ByteString.CopyFromUtf8(await File.ReadAllTextAsync(filename)), + }; + } + + static async Task Main(string[] args) + { + // go-plugin semantics depend on the Health Check service from gRPC + var health = HealthService.Get(); + health.SetStatus("plugin", HealthStatus.Serving); + + // Build a server to host the plugin over gRPC + var server = new Server + { + Ports = { { ServiceHost, ServicePort, ServerCredentials.Insecure } }, + Services = { + { HealthService.BindService(health) }, + { KV.BindService(new Plugin()) }, + }, + }; + + server.Start(); + + // Part of the go-plugin handshake: + // https://github.com/hashicorp/go-plugin/blob/master/docs/guide-plugin-write-non-go.md#4-output-handshake-information + await Console.Out.WriteAsync($"1|1|tcp|{ServiceHost}:{ServicePort}|grpc\n"); + await Console.Out.FlushAsync(); + + while (Console.Read() == -1) + await Task.Delay(1000); + + await server.ShutdownAsync(); + } + } +} diff --git a/examples/grpc/plugin-dotnet/Proto/Health.cs b/examples/grpc/plugin-dotnet/Proto/Health.cs new file mode 100644 index 00000000..a5f7db09 --- /dev/null +++ b/examples/grpc/plugin-dotnet/Proto/Health.cs @@ -0,0 +1,321 @@ +// +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Proto/health.proto +// +#pragma warning disable 1591, 0612, 3021 +#region Designer generated code + +using pb = global::Google.Protobuf; +using pbc = global::Google.Protobuf.Collections; +using pbr = global::Google.Protobuf.Reflection; +using scg = global::System.Collections.Generic; +namespace Grpc.Health.V1 { + + /// Holder for reflection information generated from Proto/health.proto + public static partial class HealthReflection { + + #region Descriptor + /// File descriptor for Proto/health.proto + public static pbr::FileDescriptor Descriptor { + get { return descriptor; } + } + private static pbr::FileDescriptor descriptor; + + static HealthReflection() { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "ChJQcm90by9oZWFsdGgucHJvdG8SDmdycGMuaGVhbHRoLnYxIiUKEkhlYWx0", + "aENoZWNrUmVxdWVzdBIPCgdzZXJ2aWNlGAEgASgJIpQBChNIZWFsdGhDaGVj", + "a1Jlc3BvbnNlEkEKBnN0YXR1cxgBIAEoDjIxLmdycGMuaGVhbHRoLnYxLkhl", + "YWx0aENoZWNrUmVzcG9uc2UuU2VydmluZ1N0YXR1cyI6Cg1TZXJ2aW5nU3Rh", + "dHVzEgsKB1VOS05PV04QABILCgdTRVJWSU5HEAESDwoLTk9UX1NFUlZJTkcQ", + "AjJaCgZIZWFsdGgSUAoFQ2hlY2sSIi5ncnBjLmhlYWx0aC52MS5IZWFsdGhD", + "aGVja1JlcXVlc3QaIy5ncnBjLmhlYWx0aC52MS5IZWFsdGhDaGVja1Jlc3Bv", + "bnNlYgZwcm90bzM=")); + descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, + new pbr::FileDescriptor[] { }, + new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] { + new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Health.V1.HealthCheckRequest), global::Grpc.Health.V1.HealthCheckRequest.Parser, new[]{ "Service" }, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Health.V1.HealthCheckResponse), global::Grpc.Health.V1.HealthCheckResponse.Parser, new[]{ "Status" }, null, new[]{ typeof(global::Grpc.Health.V1.HealthCheckResponse.Types.ServingStatus) }, null) + })); + } + #endregion + + } + #region Messages + public sealed partial class HealthCheckRequest : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new HealthCheckRequest()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pbr::MessageDescriptor Descriptor { + get { return global::Grpc.Health.V1.HealthReflection.Descriptor.MessageTypes[0]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public HealthCheckRequest() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public HealthCheckRequest(HealthCheckRequest other) : this() { + service_ = other.service_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public HealthCheckRequest Clone() { + return new HealthCheckRequest(this); + } + + /// Field number for the "service" field. + public const int ServiceFieldNumber = 1; + private string service_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string Service { + get { return service_; } + set { + service_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override bool Equals(object other) { + return Equals(other as HealthCheckRequest); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool Equals(HealthCheckRequest other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Service != other.Service) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override int GetHashCode() { + int hash = 1; + if (Service.Length != 0) hash ^= Service.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void WriteTo(pb::CodedOutputStream output) { + if (Service.Length != 0) { + output.WriteRawTag(10); + output.WriteString(Service); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int CalculateSize() { + int size = 0; + if (Service.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Service); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(HealthCheckRequest other) { + if (other == null) { + return; + } + if (other.Service.Length != 0) { + Service = other.Service; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 10: { + Service = input.ReadString(); + break; + } + } + } + } + + } + + public sealed partial class HealthCheckResponse : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new HealthCheckResponse()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pbr::MessageDescriptor Descriptor { + get { return global::Grpc.Health.V1.HealthReflection.Descriptor.MessageTypes[1]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public HealthCheckResponse() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public HealthCheckResponse(HealthCheckResponse other) : this() { + status_ = other.status_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public HealthCheckResponse Clone() { + return new HealthCheckResponse(this); + } + + /// Field number for the "status" field. + public const int StatusFieldNumber = 1; + private global::Grpc.Health.V1.HealthCheckResponse.Types.ServingStatus status_ = 0; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public global::Grpc.Health.V1.HealthCheckResponse.Types.ServingStatus Status { + get { return status_; } + set { + status_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override bool Equals(object other) { + return Equals(other as HealthCheckResponse); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool Equals(HealthCheckResponse other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Status != other.Status) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override int GetHashCode() { + int hash = 1; + if (Status != 0) hash ^= Status.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void WriteTo(pb::CodedOutputStream output) { + if (Status != 0) { + output.WriteRawTag(8); + output.WriteEnum((int) Status); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int CalculateSize() { + int size = 0; + if (Status != 0) { + size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Status); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(HealthCheckResponse other) { + if (other == null) { + return; + } + if (other.Status != 0) { + Status = other.Status; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 8: { + status_ = (global::Grpc.Health.V1.HealthCheckResponse.Types.ServingStatus) input.ReadEnum(); + break; + } + } + } + } + + #region Nested types + /// Container for nested types declared in the HealthCheckResponse message type. + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static partial class Types { + public enum ServingStatus { + [pbr::OriginalName("UNKNOWN")] Unknown = 0, + [pbr::OriginalName("SERVING")] Serving = 1, + [pbr::OriginalName("NOT_SERVING")] NotServing = 2, + } + + } + #endregion + + } + + #endregion + +} + +#endregion Designer generated code diff --git a/examples/grpc/plugin-dotnet/Proto/HealthGrpc.cs b/examples/grpc/plugin-dotnet/Proto/HealthGrpc.cs new file mode 100644 index 00000000..53497ed9 --- /dev/null +++ b/examples/grpc/plugin-dotnet/Proto/HealthGrpc.cs @@ -0,0 +1,111 @@ +// +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Proto/health.proto +// +// Original file comments: +// From: +// https://github.com/grpc/grpc/blob/master/doc/health-checking.md +// https://github.com/hashicorp/go-plugin/blob/master/docs/guide-plugin-write-non-go.md#3-add-the-grpc-health-checking-service +// +#pragma warning disable 0414, 1591 +#region Designer generated code + +using grpc = global::Grpc.Core; + +namespace Grpc.Health.V1 { + public static partial class Health + { + static readonly string __ServiceName = "grpc.health.v1.Health"; + + static readonly grpc::Marshaller __Marshaller_grpc_health_v1_HealthCheckRequest = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Health.V1.HealthCheckRequest.Parser.ParseFrom); + static readonly grpc::Marshaller __Marshaller_grpc_health_v1_HealthCheckResponse = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Grpc.Health.V1.HealthCheckResponse.Parser.ParseFrom); + + static readonly grpc::Method __Method_Check = new grpc::Method( + grpc::MethodType.Unary, + __ServiceName, + "Check", + __Marshaller_grpc_health_v1_HealthCheckRequest, + __Marshaller_grpc_health_v1_HealthCheckResponse); + + /// Service descriptor + public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor + { + get { return global::Grpc.Health.V1.HealthReflection.Descriptor.Services[0]; } + } + + /// Base class for server-side implementations of Health + public abstract partial class HealthBase + { + public virtual global::System.Threading.Tasks.Task Check(global::Grpc.Health.V1.HealthCheckRequest request, grpc::ServerCallContext context) + { + throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, "")); + } + + } + + /// Client for Health + public partial class HealthClient : grpc::ClientBase + { + /// Creates a new client for Health + /// The channel to use to make remote calls. + public HealthClient(grpc::Channel channel) : base(channel) + { + } + /// Creates a new client for Health that uses a custom CallInvoker. + /// The callInvoker to use to make remote calls. + public HealthClient(grpc::CallInvoker callInvoker) : base(callInvoker) + { + } + /// Protected parameterless constructor to allow creation of test doubles. + protected HealthClient() : base() + { + } + /// Protected constructor to allow creation of configured clients. + /// The client configuration. + protected HealthClient(ClientBaseConfiguration configuration) : base(configuration) + { + } + + public virtual global::Grpc.Health.V1.HealthCheckResponse Check(global::Grpc.Health.V1.HealthCheckRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) + { + return Check(request, new grpc::CallOptions(headers, deadline, cancellationToken)); + } + public virtual global::Grpc.Health.V1.HealthCheckResponse Check(global::Grpc.Health.V1.HealthCheckRequest request, grpc::CallOptions options) + { + return CallInvoker.BlockingUnaryCall(__Method_Check, null, options, request); + } + public virtual grpc::AsyncUnaryCall CheckAsync(global::Grpc.Health.V1.HealthCheckRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) + { + return CheckAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken)); + } + public virtual grpc::AsyncUnaryCall CheckAsync(global::Grpc.Health.V1.HealthCheckRequest request, grpc::CallOptions options) + { + return CallInvoker.AsyncUnaryCall(__Method_Check, null, options, request); + } + /// Creates a new instance of client from given ClientBaseConfiguration. + protected override HealthClient NewInstance(ClientBaseConfiguration configuration) + { + return new HealthClient(configuration); + } + } + + /// Creates service definition that can be registered with a server + /// An object implementing the server-side handling logic. + public static grpc::ServerServiceDefinition BindService(HealthBase serviceImpl) + { + return grpc::ServerServiceDefinition.CreateBuilder() + .AddMethod(__Method_Check, serviceImpl.Check).Build(); + } + + /// Register service method with a service binder with or without implementation. Useful when customizing the service binding logic. + /// Note: this method is part of an experimental API that can change or be removed without any prior notice. + /// Service methods will be bound by calling AddMethod on this object. + /// An object implementing the server-side handling logic. + public static void BindService(grpc::ServiceBinderBase serviceBinder, HealthBase serviceImpl) + { + serviceBinder.AddMethod(__Method_Check, serviceImpl == null ? null : new grpc::UnaryServerMethod(serviceImpl.Check)); + } + + } +} +#endregion diff --git a/examples/grpc/plugin-dotnet/Proto/HealthServiceImpl.cs b/examples/grpc/plugin-dotnet/Proto/HealthServiceImpl.cs new file mode 100644 index 00000000..c034f964 --- /dev/null +++ b/examples/grpc/plugin-dotnet/Proto/HealthServiceImpl.cs @@ -0,0 +1,113 @@ +// Adapted from: +// https://raw.githubusercontent.com/grpc/grpc/master/src/csharp/Grpc.HealthCheck/HealthServiceImpl.cs + +#region Copyright notice and license +// Copyright 2015 gRPC authors. +// +// 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. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Grpc.Core; +using Grpc.Core.Utils; +using Grpc.Health.V1; +using plugin_dotnet; + +namespace Grpc.HealthCheck +{ + /// + /// Implementation of a simple Health service. Useful for health checking. + /// + /// Registering service with a server: + /// + /// var serviceImpl = new HealthServiceImpl(); + /// server = new Server(); + /// server.AddServiceDefinition(Grpc.Health.V1.Health.BindService(serviceImpl)); + /// + /// + public class HealthServiceImpl : Grpc.Health.V1.Health.HealthBase, IHealthService + { + private readonly object myLock = new object(); + private readonly Dictionary statusMap = + new Dictionary(); + + // Implement the service interface, mapping to concrete implementation + void IHealthService.ClearAll() => ClearAll(); + void IHealthService.ClearStatus(string service) => ClearStatus(service); + void IHealthService.SetStatus(string service, HealthStatus status) => + SetStatus(service, (HealthCheckResponse.Types.ServingStatus)status); + + /// + /// Sets the health status for given service. + /// + /// The service. Cannot be null. + /// the health status + public void SetStatus(string service, HealthCheckResponse.Types.ServingStatus status) + { + lock (myLock) + { + statusMap[service] = status; + } + } + + /// + /// Clears health status for given service. + /// + /// The service. Cannot be null. + public void ClearStatus(string service) + { + lock (myLock) + { + statusMap.Remove(service); + } + } + + /// + /// Clears statuses for all services. + /// + public void ClearAll() + { + lock (myLock) + { + statusMap.Clear(); + } + } + + /// + /// Performs a health status check. + /// + /// The check request. + /// The call context. + /// The asynchronous response. + public override Task Check(HealthCheckRequest request, ServerCallContext context) + { + lock (myLock) + { + var service = request.Service; + + HealthCheckResponse.Types.ServingStatus status; + if (!statusMap.TryGetValue(service, out status)) + { + // TODO(jtattermusch): returning specific status from server handler is not supported yet. + throw new RpcException(new Status(StatusCode.NotFound, "")); + } + return Task.FromResult(new HealthCheckResponse { Status = status }); + } + } + } +} diff --git a/examples/grpc/plugin-dotnet/Proto/Kv.cs b/examples/grpc/plugin-dotnet/Proto/Kv.cs new file mode 100644 index 00000000..91f95bfe --- /dev/null +++ b/examples/grpc/plugin-dotnet/Proto/Kv.cs @@ -0,0 +1,565 @@ +// +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: kv.proto +// +#pragma warning disable 1591, 0612, 3021 +#region Designer generated code + +using pb = global::Google.Protobuf; +using pbc = global::Google.Protobuf.Collections; +using pbr = global::Google.Protobuf.Reflection; +using scg = global::System.Collections.Generic; +namespace Proto { + + /// Holder for reflection information generated from kv.proto + public static partial class KvReflection { + + #region Descriptor + /// File descriptor for kv.proto + public static pbr::FileDescriptor Descriptor { + get { return descriptor; } + } + private static pbr::FileDescriptor descriptor; + + static KvReflection() { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "Cghrdi5wcm90bxIFcHJvdG8iGQoKR2V0UmVxdWVzdBILCgNrZXkYASABKAki", + "HAoLR2V0UmVzcG9uc2USDQoFdmFsdWUYASABKAwiKAoKUHV0UmVxdWVzdBIL", + "CgNrZXkYASABKAkSDQoFdmFsdWUYAiABKAwiBwoFRW1wdHkyWgoCS1YSLAoD", + "R2V0EhEucHJvdG8uR2V0UmVxdWVzdBoSLnByb3RvLkdldFJlc3BvbnNlEiYK", + "A1B1dBIRLnByb3RvLlB1dFJlcXVlc3QaDC5wcm90by5FbXB0eWIGcHJvdG8z")); + descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, + new pbr::FileDescriptor[] { }, + new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] { + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.GetRequest), global::Proto.GetRequest.Parser, new[]{ "Key" }, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.GetResponse), global::Proto.GetResponse.Parser, new[]{ "Value" }, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.PutRequest), global::Proto.PutRequest.Parser, new[]{ "Key", "Value" }, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.Empty), global::Proto.Empty.Parser, null, null, null, null) + })); + } + #endregion + + } + #region Messages + public sealed partial class GetRequest : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new GetRequest()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.KvReflection.Descriptor.MessageTypes[0]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public GetRequest() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public GetRequest(GetRequest other) : this() { + key_ = other.key_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public GetRequest Clone() { + return new GetRequest(this); + } + + /// Field number for the "key" field. + public const int KeyFieldNumber = 1; + private string key_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string Key { + get { return key_; } + set { + key_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override bool Equals(object other) { + return Equals(other as GetRequest); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool Equals(GetRequest other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Key != other.Key) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override int GetHashCode() { + int hash = 1; + if (Key.Length != 0) hash ^= Key.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void WriteTo(pb::CodedOutputStream output) { + if (Key.Length != 0) { + output.WriteRawTag(10); + output.WriteString(Key); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int CalculateSize() { + int size = 0; + if (Key.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Key); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(GetRequest other) { + if (other == null) { + return; + } + if (other.Key.Length != 0) { + Key = other.Key; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 10: { + Key = input.ReadString(); + break; + } + } + } + } + + } + + public sealed partial class GetResponse : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new GetResponse()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.KvReflection.Descriptor.MessageTypes[1]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public GetResponse() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public GetResponse(GetResponse other) : this() { + value_ = other.value_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public GetResponse Clone() { + return new GetResponse(this); + } + + /// Field number for the "value" field. + public const int ValueFieldNumber = 1; + private pb::ByteString value_ = pb::ByteString.Empty; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public pb::ByteString Value { + get { return value_; } + set { + value_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override bool Equals(object other) { + return Equals(other as GetResponse); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool Equals(GetResponse other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Value != other.Value) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override int GetHashCode() { + int hash = 1; + if (Value.Length != 0) hash ^= Value.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void WriteTo(pb::CodedOutputStream output) { + if (Value.Length != 0) { + output.WriteRawTag(10); + output.WriteBytes(Value); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int CalculateSize() { + int size = 0; + if (Value.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeBytesSize(Value); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(GetResponse other) { + if (other == null) { + return; + } + if (other.Value.Length != 0) { + Value = other.Value; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 10: { + Value = input.ReadBytes(); + break; + } + } + } + } + + } + + public sealed partial class PutRequest : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new PutRequest()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.KvReflection.Descriptor.MessageTypes[2]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public PutRequest() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public PutRequest(PutRequest other) : this() { + key_ = other.key_; + value_ = other.value_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public PutRequest Clone() { + return new PutRequest(this); + } + + /// Field number for the "key" field. + public const int KeyFieldNumber = 1; + private string key_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string Key { + get { return key_; } + set { + key_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// Field number for the "value" field. + public const int ValueFieldNumber = 2; + private pb::ByteString value_ = pb::ByteString.Empty; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public pb::ByteString Value { + get { return value_; } + set { + value_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override bool Equals(object other) { + return Equals(other as PutRequest); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool Equals(PutRequest other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Key != other.Key) return false; + if (Value != other.Value) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override int GetHashCode() { + int hash = 1; + if (Key.Length != 0) hash ^= Key.GetHashCode(); + if (Value.Length != 0) hash ^= Value.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void WriteTo(pb::CodedOutputStream output) { + if (Key.Length != 0) { + output.WriteRawTag(10); + output.WriteString(Key); + } + if (Value.Length != 0) { + output.WriteRawTag(18); + output.WriteBytes(Value); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int CalculateSize() { + int size = 0; + if (Key.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Key); + } + if (Value.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeBytesSize(Value); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(PutRequest other) { + if (other == null) { + return; + } + if (other.Key.Length != 0) { + Key = other.Key; + } + if (other.Value.Length != 0) { + Value = other.Value; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 10: { + Key = input.ReadString(); + break; + } + case 18: { + Value = input.ReadBytes(); + break; + } + } + } + } + + } + + public sealed partial class Empty : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Empty()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.KvReflection.Descriptor.MessageTypes[3]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Empty() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Empty(Empty other) : this() { + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Empty Clone() { + return new Empty(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override bool Equals(object other) { + return Equals(other as Empty); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool Equals(Empty other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override int GetHashCode() { + int hash = 1; + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void WriteTo(pb::CodedOutputStream output) { + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int CalculateSize() { + int size = 0; + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(Empty other) { + if (other == null) { + return; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + } + } + } + + } + + #endregion + +} + +#endregion Designer generated code diff --git a/examples/grpc/plugin-dotnet/Proto/KvGrpc.cs b/examples/grpc/plugin-dotnet/Proto/KvGrpc.cs new file mode 100644 index 00000000..4db952d3 --- /dev/null +++ b/examples/grpc/plugin-dotnet/Proto/KvGrpc.cs @@ -0,0 +1,138 @@ +// +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: kv.proto +// +#pragma warning disable 0414, 1591 +#region Designer generated code + +using grpc = global::Grpc.Core; + +namespace Proto { + public static partial class KV + { + static readonly string __ServiceName = "proto.KV"; + + static readonly grpc::Marshaller __Marshaller_proto_GetRequest = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Proto.GetRequest.Parser.ParseFrom); + static readonly grpc::Marshaller __Marshaller_proto_GetResponse = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Proto.GetResponse.Parser.ParseFrom); + static readonly grpc::Marshaller __Marshaller_proto_PutRequest = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Proto.PutRequest.Parser.ParseFrom); + static readonly grpc::Marshaller __Marshaller_proto_Empty = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Proto.Empty.Parser.ParseFrom); + + static readonly grpc::Method __Method_Get = new grpc::Method( + grpc::MethodType.Unary, + __ServiceName, + "Get", + __Marshaller_proto_GetRequest, + __Marshaller_proto_GetResponse); + + static readonly grpc::Method __Method_Put = new grpc::Method( + grpc::MethodType.Unary, + __ServiceName, + "Put", + __Marshaller_proto_PutRequest, + __Marshaller_proto_Empty); + + /// Service descriptor + public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor + { + get { return global::Proto.KvReflection.Descriptor.Services[0]; } + } + + /// Base class for server-side implementations of KV + public abstract partial class KVBase + { + public virtual global::System.Threading.Tasks.Task Get(global::Proto.GetRequest request, grpc::ServerCallContext context) + { + throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, "")); + } + + public virtual global::System.Threading.Tasks.Task Put(global::Proto.PutRequest request, grpc::ServerCallContext context) + { + throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, "")); + } + + } + + /// Client for KV + public partial class KVClient : grpc::ClientBase + { + /// Creates a new client for KV + /// The channel to use to make remote calls. + public KVClient(grpc::Channel channel) : base(channel) + { + } + /// Creates a new client for KV that uses a custom CallInvoker. + /// The callInvoker to use to make remote calls. + public KVClient(grpc::CallInvoker callInvoker) : base(callInvoker) + { + } + /// Protected parameterless constructor to allow creation of test doubles. + protected KVClient() : base() + { + } + /// Protected constructor to allow creation of configured clients. + /// The client configuration. + protected KVClient(ClientBaseConfiguration configuration) : base(configuration) + { + } + + public virtual global::Proto.GetResponse Get(global::Proto.GetRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) + { + return Get(request, new grpc::CallOptions(headers, deadline, cancellationToken)); + } + public virtual global::Proto.GetResponse Get(global::Proto.GetRequest request, grpc::CallOptions options) + { + return CallInvoker.BlockingUnaryCall(__Method_Get, null, options, request); + } + public virtual grpc::AsyncUnaryCall GetAsync(global::Proto.GetRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) + { + return GetAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken)); + } + public virtual grpc::AsyncUnaryCall GetAsync(global::Proto.GetRequest request, grpc::CallOptions options) + { + return CallInvoker.AsyncUnaryCall(__Method_Get, null, options, request); + } + public virtual global::Proto.Empty Put(global::Proto.PutRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) + { + return Put(request, new grpc::CallOptions(headers, deadline, cancellationToken)); + } + public virtual global::Proto.Empty Put(global::Proto.PutRequest request, grpc::CallOptions options) + { + return CallInvoker.BlockingUnaryCall(__Method_Put, null, options, request); + } + public virtual grpc::AsyncUnaryCall PutAsync(global::Proto.PutRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) + { + return PutAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken)); + } + public virtual grpc::AsyncUnaryCall PutAsync(global::Proto.PutRequest request, grpc::CallOptions options) + { + return CallInvoker.AsyncUnaryCall(__Method_Put, null, options, request); + } + /// Creates a new instance of client from given ClientBaseConfiguration. + protected override KVClient NewInstance(ClientBaseConfiguration configuration) + { + return new KVClient(configuration); + } + } + + /// Creates service definition that can be registered with a server + /// An object implementing the server-side handling logic. + public static grpc::ServerServiceDefinition BindService(KVBase serviceImpl) + { + return grpc::ServerServiceDefinition.CreateBuilder() + .AddMethod(__Method_Get, serviceImpl.Get) + .AddMethod(__Method_Put, serviceImpl.Put).Build(); + } + + /// Register service method with a service binder with or without implementation. Useful when customizing the service binding logic. + /// Note: this method is part of an experimental API that can change or be removed without any prior notice. + /// Service methods will be bound by calling AddMethod on this object. + /// An object implementing the server-side handling logic. + public static void BindService(grpc::ServiceBinderBase serviceBinder, KVBase serviceImpl) + { + serviceBinder.AddMethod(__Method_Get, serviceImpl == null ? null : new grpc::UnaryServerMethod(serviceImpl.Get)); + serviceBinder.AddMethod(__Method_Put, serviceImpl == null ? null : new grpc::UnaryServerMethod(serviceImpl.Put)); + } + + } +} +#endregion diff --git a/examples/grpc/plugin-dotnet/Proto/health.proto b/examples/grpc/plugin-dotnet/Proto/health.proto new file mode 100644 index 00000000..cf9eacb8 --- /dev/null +++ b/examples/grpc/plugin-dotnet/Proto/health.proto @@ -0,0 +1,24 @@ +// From: +// https://github.com/grpc/grpc/blob/master/doc/health-checking.md +// https://github.com/hashicorp/go-plugin/blob/master/docs/guide-plugin-write-non-go.md#3-add-the-grpc-health-checking-service + +syntax = "proto3"; + +package grpc.health.v1; + +message HealthCheckRequest { + string service = 1; +} + +message HealthCheckResponse { + enum ServingStatus { + UNKNOWN = 0; + SERVING = 1; + NOT_SERVING = 2; + } + ServingStatus status = 1; +} + +service Health { + rpc Check(HealthCheckRequest) returns (HealthCheckResponse); +} diff --git a/examples/grpc/plugin-dotnet/README.md b/examples/grpc/plugin-dotnet/README.md new file mode 100644 index 00000000..41506f28 --- /dev/null +++ b/examples/grpc/plugin-dotnet/README.md @@ -0,0 +1,22 @@ +# KV Example + +This example builds a plugin in C# for the [KV Example](https://github.com/hashicorp/go-plugin/tree/master/examples/grpc) in the [`go-plugin`](https://github.com/hashicorp/go-plugin) system over RPC. + +To build and use this example, first follow the [directions](../README.md) for building the main +CLI for the example. + +Next, build this C#-example after downloading and installing the *latest* [.NET Core SDK](https://dotnet.microsoft.com/download): + +```pwsh + +## From the project root +$ dotnet publish -c Release -o go-plugins + +## Tell the KV CLI to use the C# plugin +$ export KV_PLUGIN="dotnet ./go-plugins/plugin-dotnet.dll" + +## Write and Read +$ ../kv put hello world +$ ../kv get hello + +``` diff --git a/examples/grpc/plugin-dotnet/plugin-dotnet.csproj b/examples/grpc/plugin-dotnet/plugin-dotnet.csproj new file mode 100644 index 00000000..557ed248 --- /dev/null +++ b/examples/grpc/plugin-dotnet/plugin-dotnet.csproj @@ -0,0 +1,26 @@ + + + + Exe + netcoreapp2.2 + plugin_dotnet + LATEST + + + + + + + + + + true + + + + + + + + +