diff --git a/docker/kubernetes-tentacle/Dockerfile b/docker/kubernetes-tentacle/Dockerfile index 10e51c3c4..285619f34 100644 --- a/docker/kubernetes-tentacle/Dockerfile +++ b/docker/kubernetes-tentacle/Dockerfile @@ -18,6 +18,8 @@ ARG TARGETOS ARG TARGETVARIANT EXPOSE 10933 +EXPOSE 5001 +EXPOSE 5002 COPY docker/kubernetes-tentacle/scripts/* /scripts/ COPY --from=bootstrapRunnerBuilder bootstrapRunner/bin/bootstrapRunner /bootstrapRunner diff --git a/installer/Octopus.Tentacle.Installer/Product.wxs b/installer/Octopus.Tentacle.Installer/Product.wxs index 6f90546d2..4794bd98f 100644 --- a/installer/Octopus.Tentacle.Installer/Product.wxs +++ b/installer/Octopus.Tentacle.Installer/Product.wxs @@ -14,7 +14,7 @@ - Product/Version : Change this every major build - Product/UpgradeCode : Never change this --> - + diff --git a/source/Octopus.Manager.Tentacle/Octopus.Manager.Tentacle.csproj b/source/Octopus.Manager.Tentacle/Octopus.Manager.Tentacle.csproj index e73fcf682..d6f6a1902 100644 --- a/source/Octopus.Manager.Tentacle/Octopus.Manager.Tentacle.csproj +++ b/source/Octopus.Manager.Tentacle/Octopus.Manager.Tentacle.csproj @@ -123,7 +123,7 @@ - + diff --git a/source/Octopus.Tentacle.Tests/Commands/RunAgentCommandFixture.cs b/source/Octopus.Tentacle.Tests/Commands/RunAgentCommandFixture.cs index 5f0f36859..d3e504f61 100644 --- a/source/Octopus.Tentacle.Tests/Commands/RunAgentCommandFixture.cs +++ b/source/Octopus.Tentacle.Tests/Commands/RunAgentCommandFixture.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using Autofac; using NSubstitute; using NUnit.Framework; using Octopus.Diagnostics; @@ -60,7 +61,8 @@ public override void SetUp() Substitute.For(), new AppVersion(GetType().Assembly), Substitute.For(), - backgroundTasks.Select(bt => new Lazy(() => bt)).ToList()); + backgroundTasks.Select(bt => new Lazy(() => bt)).ToList(), + new ContainerBuilder().Build()); selector.Current.Returns(new ApplicationInstanceConfiguration("MyTentacle", null, null, null)); } diff --git a/source/Octopus.Tentacle/Commands/RunAgentCommand.cs b/source/Octopus.Tentacle/Commands/RunAgentCommand.cs index 79151329d..dd935be2c 100644 --- a/source/Octopus.Tentacle/Commands/RunAgentCommand.cs +++ b/source/Octopus.Tentacle/Commands/RunAgentCommand.cs @@ -4,7 +4,20 @@ using System.Linq; using System.Runtime.InteropServices; using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; using System.Security.Principal; +using Autofac; +using Autofac.Extensions.DependencyInjection; +#if !NETFRAMEWORK +using Microsoft.AspNetCore.Server.Kestrel.Https; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Server.Kestrel.Core; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Octopus.Tentacle.Communications.gRPC; +#endif using Octopus.Diagnostics; using Octopus.Tentacle.Background; using Octopus.Tentacle.Communications; @@ -41,6 +54,11 @@ public class RunAgentCommand : AbstractStandardCommand public override bool CanRunAsService => true; + readonly ILifetimeScope container; + +#if !NETFRAMEWORK + private IHost host; +#endif public RunAgentCommand( Lazy halibut, Lazy configuration, @@ -53,7 +71,7 @@ public RunAgentCommand( IWindowsLocalAdminRightsChecker windowsLocalAdminRightsChecker, AppVersion appVersion, ILogFileOnlyLogger logFileOnlyLogger, - IEnumerable> backgroundTasks) : base(selector, log, logFileOnlyLogger) + IEnumerable> backgroundTasks, ILifetimeScope container) : base(selector, log, logFileOnlyLogger) { this.halibut = halibut; this.configuration = configuration; @@ -66,6 +84,11 @@ public RunAgentCommand( this.windowsLocalAdminRightsChecker = windowsLocalAdminRightsChecker; this.appVersion = appVersion; this.backgroundTasks = backgroundTasks; + this.container = container; + +#if !NETFRAMEWORK + host = CreateHostBuilder().Build(); +#endif Options.Add("wait=", "Delay (ms) before starting", arg => wait = int.Parse(arg)); Options.Add("console", "Don't attempt to run as a service, even if the user is non-interactive", v => @@ -74,6 +97,74 @@ public RunAgentCommand( // This option is added to show help }); } +#if !NETFRAMEWORK + private IHostBuilder CreateHostBuilder() => + Host.CreateDefaultBuilder() + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseKestrel(options => + { + // options.ConfigureHttpsDefaults(httpsOptions => + // { + // httpsOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate; + // httpsOptions.ClientCertificateValidation = (certificate, _, __) => + // { + // using var cert = new X509Certificate2(certificate.Export(X509ContentType.Cert)); + // return configuration.Value.TrustedOctopusServers.Any(serverConfiguration => serverConfiguration.Thumbprint == cert.Thumbprint); + // }; + // }); + options.Limits.MaxRequestBodySize = null; + + options.ListenAnyIP(5001, listenOptions => + { + listenOptions.Protocols = HttpProtocols.Http1; + // listenOptions.UseHttps(configuration.Value.TentacleCertificate!); + }); + options.ListenAnyIP(5002, listenOptions => + { + listenOptions.Protocols = HttpProtocols.Http2; + // listenOptions.UseHttps(configuration.Value.TentacleCertificate!); + }); + }); + webBuilder.UseStartup(); + }).UseServiceProviderFactory(new AutofacChildLifetimeScopeServiceProviderFactory(container)); + + class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddGrpcReflection(); + services.AddGrpc(options => + { + options.MaxReceiveMessageSize = null; + options.MaxSendMessageSize = null; + }); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + // if (env.IsDevelopment()) + // { + app.UseDeveloperExceptionPage(); + // } + + app.UseRouting(); + app.UseEndpoints(endpoints => + { + endpoints.MapGrpcReflectionService(); + endpoints.MapGrpcService(); + endpoints.MapGet("/", async context => + { + await context.Response.WriteAsync( + "Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909"); + }); + }); + } + } +#endif protected override void Start() { @@ -128,6 +219,9 @@ protected override void Start() halibut.Value.Start(); halibutHasStarted = true; +#if !NETFRAMEWORK + host.Start(); +#endif foreach (var backgroundTaskLazy in backgroundTasks) { @@ -156,10 +250,16 @@ protected override void Stop() halibut.Value.Stop(); } +#if !NETFRAMEWORK + Console.WriteLine("Stopping host"); + host.StopAsync().GetAwaiter().GetResult(); + host.Dispose(); +#endif + foreach (var backgroundTaskLazy in backgroundTasks.Where(bt => bt.IsValueCreated)) { backgroundTaskLazy.Value.Stop(); } } } -} +} \ No newline at end of file diff --git a/source/Octopus.Tentacle/Communications/HalibutInitializer.cs b/source/Octopus.Tentacle/Communications/HalibutInitializer.cs index 8daec4ccc..c61f819c7 100644 --- a/source/Octopus.Tentacle/Communications/HalibutInitializer.cs +++ b/source/Octopus.Tentacle/Communications/HalibutInitializer.cs @@ -1,11 +1,14 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Threading; +using System.Threading.Tasks; using Halibut; using Halibut.Diagnostics; +using NuGet.Packaging; using Octopus.Client.Model; using Octopus.Client.Model.Endpoints; using Octopus.Diagnostics; @@ -20,6 +23,7 @@ public class HalibutInitializer : IHalibutInitializer readonly HalibutRuntime halibut; readonly IProxyConfigParser proxyConfigParser; readonly ISystemLog log; + public static readonly IList ServiceEndPoints = new List(); public HalibutInitializer(IWritableTentacleConfiguration configuration, HalibutRuntime halibut, IProxyConfigParser proxyConfigParser, ISystemLog log) { @@ -35,8 +39,10 @@ public void Start() TrustOctopusServers(); - AddPollingEndpoints(); + ServiceEndPoints.AddRange(AddPollingEndpoints()); + // KeepPingingServersForNoReason(endpoints); + if (configuration.NoListen) { log.Info("Agent will not listen on any TCP ports"); @@ -85,7 +91,7 @@ void TrustOctopusServers() - void AddPollingEndpoints() + IEnumerable AddPollingEndpoints() { foreach (var pollingEndPoint in GetOctopusServersToPoll()) { @@ -110,6 +116,8 @@ void AddPollingEndpoints() for (var i = 0; i < connectionCount; i++) { halibut.Poll(new Uri(pollingEndPoint.SubscriptionId), serviceEndPoint, CancellationToken.None); + yield return serviceEndPoint; + //yield return new ServiceEndPoint(new Uri(pollingEndPoint.SubscriptionId), pollingEndPoint.Thumbprint, halibutTimeoutsAndLimits); } } } @@ -174,4 +182,4 @@ public void Stop() { } } -} +} \ No newline at end of file diff --git a/source/Octopus.Tentacle/Communications/IEchoService.cs b/source/Octopus.Tentacle/Communications/IEchoService.cs new file mode 100644 index 000000000..543ee8158 --- /dev/null +++ b/source/Octopus.Tentacle/Communications/IEchoService.cs @@ -0,0 +1,20 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Octopus.Tentacle.Communications +{ + public interface IMyEchoService + { + string SayHello(string name); + } + + public interface IAsyncClientMyEchoService + { + Task SayHelloAsync(string name); + } + + public interface IAsyncMyEchoService + { + Task SayHelloAsync(string name, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/source/Octopus.Tentacle/Communications/TentacleCommunicationsModule.cs b/source/Octopus.Tentacle/Communications/TentacleCommunicationsModule.cs index 7491292de..a51c16e6d 100644 --- a/source/Octopus.Tentacle/Communications/TentacleCommunicationsModule.cs +++ b/source/Octopus.Tentacle/Communications/TentacleCommunicationsModule.cs @@ -17,7 +17,7 @@ protected override void Load(ContainerBuilder builder) base.Load(builder); builder.RegisterType().As(); - builder.RegisterType().As(); + builder.RegisterType().As().SingleInstance(); builder.RegisterType().AsImplementedInterfaces().SingleInstance(); builder.Register(c => diff --git a/source/Octopus.Tentacle/Communications/gRPC/GreeterService.cs b/source/Octopus.Tentacle/Communications/gRPC/GreeterService.cs new file mode 100644 index 000000000..c48ed8897 --- /dev/null +++ b/source/Octopus.Tentacle/Communications/gRPC/GreeterService.cs @@ -0,0 +1,37 @@ +#if !NETFRAMEWORK +using System; +using System.Linq; +using System.Threading.Tasks; +using Grpc.Core; +using Halibut; + +namespace Octopus.Tentacle.Communications.gRPC +{ + public class GreeterService: Greeter.GreeterBase + { + readonly HalibutRuntime halibut; + + public GreeterService(HalibutRuntime halibut) + { + this.halibut = halibut; + } + + + public override Task SayHello( + HelloRequest request, ServerCallContext context) + { + var client = halibut.CreateAsyncClient(HalibutInitializer.ServiceEndPoints.First()); + + Task.Run(async () => + { + await client.SayHelloAsync("Hello from: " + request.Name); + }); + + return Task.FromResult(new HelloReply + { + Message = "Hello " + request.Name + }); + } + } +} +#endif \ No newline at end of file diff --git a/source/Octopus.Tentacle/Configuration/ConfigurationModule.cs b/source/Octopus.Tentacle/Configuration/ConfigurationModule.cs index f508c2434..4a6f5a39c 100644 --- a/source/Octopus.Tentacle/Configuration/ConfigurationModule.cs +++ b/source/Octopus.Tentacle/Configuration/ConfigurationModule.cs @@ -67,6 +67,7 @@ protected override void Load(ContainerBuilder builder) .As() .SingleInstance(); +#pragma warning disable CS8714 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'notnull' constraint. builder.Register(c => { var selector = c.Resolve(); @@ -82,6 +83,7 @@ protected override void Load(ContainerBuilder builder) }) .As() .SingleInstance(); +#pragma warning restore CS8714 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'notnull' constraint. builder.RegisterType() .As() diff --git a/source/Octopus.Tentacle/Octopus.Tentacle.csproj b/source/Octopus.Tentacle/Octopus.Tentacle.csproj index 24a4478e2..1f2569529 100644 --- a/source/Octopus.Tentacle/Octopus.Tentacle.csproj +++ b/source/Octopus.Tentacle/Octopus.Tentacle.csproj @@ -50,12 +50,21 @@ + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + - + + @@ -127,4 +136,13 @@ + + + + + + + + + diff --git a/source/Octopus.Tentacle/Program.cs b/source/Octopus.Tentacle/Program.cs index 4a349dc35..2133227cf 100644 --- a/source/Octopus.Tentacle/Program.cs +++ b/source/Octopus.Tentacle/Program.cs @@ -92,6 +92,8 @@ public override IContainer BuildContainer(StartUpInstanceRequest startUpInstance builder.RegisterCommand("list-instances", "Lists all installed Tentacle instances"); builder.RegisterCommand("version", "Show the Tentacle version information"); builder.RegisterCommand("show-configuration", "Outputs the Tentacle configuration"); + + builder.RegisterModule(new CommandModule()); return builder.Build(); } diff --git a/source/Octopus.Tentacle/Protos/greet.proto b/source/Octopus.Tentacle/Protos/greet.proto new file mode 100644 index 000000000..083ba80b5 --- /dev/null +++ b/source/Octopus.Tentacle/Protos/greet.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +service Greeter { + rpc SayHello (HelloRequest) returns (HelloReply); +} + +message HelloRequest { + string name = 1; +} + +message HelloReply { + string message = 1; +} \ No newline at end of file diff --git a/source/Octopus.Tentacle/Startup/OctopusProgram.cs b/source/Octopus.Tentacle/Startup/OctopusProgram.cs index bc7b6687f..4ee77eaea 100644 --- a/source/Octopus.Tentacle/Startup/OctopusProgram.cs +++ b/source/Octopus.Tentacle/Startup/OctopusProgram.cs @@ -605,11 +605,11 @@ static string[] TryResolveCommand( protected virtual void RegisterAdditionalModules(IContainer builtContainer) { - var builder = new ContainerBuilder(); - builder.RegisterModule(new CommandModule()); -#pragma warning disable 618 - builder.Update(builtContainer); -#pragma warning restore 618 + // var builder = new ContainerBuilder(); +// builder.RegisterModule(new CommandModule()); +// #pragma warning disable 618 +// builder.Update(builtContainer); +// #pragma warning restore 618 } static string ParseCommandName(string[] args)