public Test_RegistrationError(TemporalFixture fixture) { var settings = new TemporalSettings() { Namespace = TemporalFixture.Namespace, ProxyLogLevel = TemporalTestHelper.ProxyLogLevel, CreateNamespace = true, Debug = TemporalTestHelper.Debug, DebugPrelaunched = TemporalTestHelper.DebugPrelaunched, DebugDisableHeartbeats = TemporalTestHelper.DebugDisableHeartbeats, ClientIdentity = TemporalTestHelper.ClientIdentity }; if (fixture.Start(settings, composeFile: TemporalTestHelper.TemporalStackDefinition, reconnect: true, keepRunning: TemporalTestHelper.KeepTemporalServerOpen) == TestFixtureStatus.Started) { this.fixture = fixture; this.client = fixture.Client; this.proxyClient = new HttpClient() { BaseAddress = client.ProxyUri }; // NOTE: We're not auto-registering workflows and activities or starting // workers for these unit tests. } else { this.fixture = fixture; this.client = fixture.Client; this.proxyClient = new HttpClient() { BaseAddress = client.ProxyUri }; } }
public static async Task Main(string[] args) { // Connect to Temporal var settings = new TemporalSettings() { Namespace = "my-namespace", TaskQueue = "my-tasks", CreateNamespace = true, HostPort = "localhost:7933" }; using (var client = await TemporalClient.ConnectAsync(settings)) { // Create a worker and register the workflow and activity // implementations to let Temporal know we're open for business. var worker = await client.NewWorkerAsync(); await worker.RegisterAssemblyAsync(Assembly.GetExecutingAssembly()); await worker.StartAsync(); // Invoke the workflow. var workflowStub = client.NewWorkflowStub <IEmailWorkflow>(); await workflowStub.SendMessagesAsync(); } }
public static async Task Main(string[] args) { // Connect to Temporal var settings = new TemporalSettings() { Namespace = "my-namespace", CreateNamespace = true, HostPort = "localhost:7933" }; using (var client = await TemporalClient.ConnectAsync(settings)) { // Create a worker and register the workflow and activity // implementations to let Temporal know we're open for business. var worker = await client.NewWorkerAsync(); await worker.RegisterWorkflowAsync <HelloWorkflow>(); await worker.StartAsync(); // Invoke your workflow. var stub = client.NewWorkflowStub <IHelloWorkflow>(); Console.WriteLine(await stub.HelloAsync("Jeff")); } }
/// <summary> /// <para> /// Starts a Temporal compose application if it's not already running. You'll generally want /// to call this in your test class constructor instead of <see cref="ITestFixture.Start(Action)"/>. /// </para> /// <note> /// You'll need to call <see cref="StartAsComposed(TemporalSettings, string, string, string, Neon.Diagnostics.LogLevel, bool, bool, bool, bool)"/> /// instead when this fixture is being added to a <see cref="ComposedFixture"/>. /// </note> /// </summary> /// <param name="settings">Optional Temporal settings.</param> /// <param name="name">Optionally specifies the Docker compose application name (defaults to <c>temporal-dev</c>).</param> /// <param name="composeFile"> /// <para> /// Optionally specifies the Temporal Docker compose file text. This defaults to /// <see cref="DefaultComposeFile"/> which configures Temporal server to start with /// a new Cassandra database instance listening on port <b>9042</b> as well as the /// Temporal web UI running on port <b>8088</b>. Temporal server is listening on /// its standard gRPC port <b>7233</b>. /// </para> /// <para> /// You may specify your own Docker compose text file to customize this by configuring /// a different backend database, etc. /// </para> /// </param> /// <param name="defaultNamespace">Optionally specifies the default namespace for the fixture's client. This defaults to <b>test-namespace</b>.</param> /// <param name="logLevel">Specifies the Temporal log level. This defaults to <see cref="Neon.Diagnostics.LogLevel.None"/>.</param> /// <param name="reconnect"> /// Optionally specifies that a new Temporal connection <b>should</b> be established for each /// unit test case. By default, the same connection will be reused which will save about a /// second per test case. /// </param> /// <param name="keepRunning"> /// Optionally indicates that the application should remain running after the fixture is disposed. /// This is handy for using the Temporal web UI for port mortems after tests have completed. /// </param> /// <param name="noClient"> /// Optionally disables establishing a client connection when <c>true</c> /// is passed. The <see cref="Client"/> and <see cref="HttpClient"/> properties /// will be set to <c>null</c> in this case. /// </param> /// <param name="noReset"> /// Optionally prevents the fixture from calling <see cref="TemporalClient.Reset()"/> to /// put the Temporal client library into its initial state before the fixture starts as well /// as when the fixture itself is reset. /// </param> /// <returns> /// <see cref="TestFixtureStatus.Started"/> if the fixture wasn't previously started and /// this method call started it or <see cref="TestFixtureStatus.AlreadyRunning"/> if the /// fixture was already running. /// </returns> /// <remarks> /// <note> /// Some of the <paramref name="settings"/> properties will be ignored including /// <see cref="TemporalSettings.HostPort"/>. This will be replaced by the local /// endpoint for the Temporal application. Also, the fixture will connect to the /// <b>default</b> Temporal namespace by default (unless another is specified). /// </note> /// <note> /// A fresh Temporal client <see cref="Client"/> will be established every time this /// fixture is started, regardless of whether the fixture has already been started. This /// ensures that each unit test will start with a client in the default state. /// </note> /// </remarks> public TestFixtureStatus Start( TemporalSettings settings = null, string name = "temporal-dev", string composeFile = DefaultComposeFile, string defaultNamespace = Namespace, LogLevel logLevel = LogLevel.None, bool reconnect = false, bool keepRunning = false, bool noClient = false, bool noReset = false) { Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(composeFile), nameof(composeFile)); return(base.Start( () => { StartAsComposed( settings: settings, name: name, composeFile: composeFile, defaultNamespace: defaultNamespace, logLevel: logLevel, reconnect: reconnect, keepRunning: keepRunning, noClient: noClient, noReset: noReset); })); }
public Test_Replay(TemporalFixture fixture) { TestHelper.ResetDocker(this.GetType()); var settings = new TemporalSettings() { Namespace = TemporalFixture.Namespace, ProxyLogLevel = TemporalTestHelper.ProxyLogLevel, CreateNamespace = true, Debug = TemporalTestHelper.Debug, DebugPrelaunched = TemporalTestHelper.DebugPrelaunched, DebugDisableHeartbeats = TemporalTestHelper.DebugDisableHeartbeats, ClientIdentity = TemporalTestHelper.ClientIdentity, TaskQueue = TemporalTestHelper.TaskQueue }; if (fixture.Start(settings, composeFile: TemporalTestHelper.TemporalStackDefinition, reconnect: true, keepRunning: TemporalTestHelper.KeepTemporalServerOpen) == TestFixtureStatus.Started) { this.fixture = fixture; this.client = fixture.Client; // Create a worker and register the workflow and activity // implementations to let Temporal know we're open for business. var worker = client.NewWorkerAsync().Result; worker.RegisterAssemblyAsync(Assembly.GetExecutingAssembly()).WaitWithoutAggregate(); worker.StartAsync().WaitWithoutAggregate(); } else { this.fixture = fixture; this.client = fixture.Client; } }
public Test_SignalChecks(TemporalFixture fixture) { TestHelper.ResetDocker(this.GetType()); var settings = new TemporalSettings() { Namespace = TemporalFixture.Namespace, ProxyLogLevel = TemporalTestHelper.ProxyLogLevel, CreateNamespace = true, Debug = TemporalTestHelper.Debug, DebugPrelaunched = TemporalTestHelper.DebugPrelaunched, DebugDisableHeartbeats = TemporalTestHelper.DebugDisableHeartbeats, ClientIdentity = TemporalTestHelper.ClientIdentity }; if (fixture.Start(settings, composeFile: TemporalTestHelper.TemporalStackDefinition, reconnect: true, keepRunning: TemporalTestHelper.KeepTemporalServerOpen) == TestFixtureStatus.Started) { this.fixture = fixture; this.client = fixture.Client; this.proxyClient = new HttpClient() { BaseAddress = client.ProxyUri }; } else { this.fixture = fixture; this.client = fixture.Client; this.proxyClient = new HttpClient() { BaseAddress = client.ProxyUri }; } }
public static async Task Main(params string[] args) { var settings = new TemporalSettings() { // This specifies the default namespace for operations initiated by the // client connected below (this can be overridden for specific // operations). Namespace = "Acme-PROD", // Host/port for at least one of the Temporal cluster servers: HostPort = "localhost:7933" }; using (var client = await TemporalClient.ConnectAsync(settings)) { // Create a typed stub for the remote workflow. var stub = client.NewWorkflowStub <IMailer>(); // Execute the workflow. var success = await stub.SendEmail("*****@*****.**", "Test subject", "This is a test email."); if (success) { Console.WriteLine("Email SENT!"); } else { Console.WriteLine("Email FAILED!"); } } }
public Test_EndToEnd(TemporalFixture fixture, ITestOutputHelper outputHelper) { TestHelper.ResetDocker(this.GetType()); testWriter = new TestOutputWriter(outputHelper); // Configure a service for activity dependency injection testing if it doesn't // already exist. if (NeonHelper.ServiceContainer.GetService <ActivityDependency>() == null) { NeonHelper.ServiceContainer.AddSingleton(typeof(ActivityDependency), new ActivityDependency() { Hello = "World!" }); } // Initialize the Cadence fixture. var settings = new TemporalSettings() { Namespace = TemporalFixture.Namespace, ProxyLogLevel = TemporalTestHelper.ProxyLogLevel, CreateNamespace = true, Debug = TemporalTestHelper.Debug, DebugPrelaunched = TemporalTestHelper.DebugPrelaunched, DebugDisableHeartbeats = TemporalTestHelper.DebugDisableHeartbeats, ClientIdentity = TemporalTestHelper.ClientIdentity, TaskQueue = TemporalTestHelper.TaskQueue }; if (fixture.Start(settings, composeFile: TemporalTestHelper.TemporalStackDefinition, reconnect: true, keepRunning: TemporalTestHelper.KeepTemporalServerOpen) == TestFixtureStatus.Started) { this.fixture = fixture; this.client = fixture.Client; // Create a worker and register the workflow and activity // implementations to let Temporal know we're open for business. var worker = client.NewWorkerAsync().Result; worker.RegisterAssemblyAsync(Assembly.GetExecutingAssembly()).WaitWithoutAggregate(); worker.StartAsync().WaitWithoutAggregate(); } else { this.fixture = fixture; this.client = fixture.Client; } }
public static async Task UntypedStub() { #region code_untyped var settings = new TemporalSettings() { // This specifies the default namespace for operations initiated by the // client connected below (this can be overridden for specific // operations). Namespace = "Acme-PROD", // Host/port for at least one of the Temporal cluster servers: HostPort = "localhost:7933" }; using (var client = await TemporalClient.ConnectAsync(settings)) { // Create an untyped stub for the remote workflow. var stub = client.NewUntypedWorkflowStub("SendEmail"); // Start the workflow. Note that we need to take to ensure that the number, order // and types of the parameters match what the GOLANG workflow implementation expects. // Untyped workflow stub arguments cannot be checked be the C# compiler. // // This method returns a [WorkflowExecution] which includes the workflow and // run IDs. We're not using these here, but real applications may want to // persist this so that could check on long-running workflows later. var execution = await stub.StartAsync("*****@*****.**", "Test subject", "This is a test email."); // Wait for the workflow to complete and return it's result. Note that we need // to explicitly specify the result [bool] type as a generic type parameter. // You need to ensure that this matches the workflow implementation as well. var success = await stub.GetResultAsync <bool>(); if (success) { Console.WriteLine("Email SENT!"); } else { Console.WriteLine("Email FAILED!"); } } #endregion }
public static async Task Main(string[] args) { // Initialize the logger. LogManager.Default.SetLogLevel("info"); logger = LogManager.Default.GetLogger(typeof(Program)); logger.LogInfo("Starting workflow service"); try { // Connect to Temporal var settings = new TemporalSettings() { Namespace = "my-namespace", CreateNamespace = true, HostPort = "localhost:7933" }; using (var client = await TemporalClient.ConnectAsync(settings)) { // Create a worker and register the workflow and activity // implementations to let Temporal know we're open for business. var worker = await client.NewWorkerAsync(); await worker.RegisterAssemblyAsync(System.Reflection.Assembly.GetExecutingAssembly()); await worker.StartAsync(); // Spin forever, processing workflows and activities assigned by Temporal. while (true) { await Task.Delay(TimeSpan.FromMinutes(5)); } } } catch (Exception e) { logger.LogError(e); } finally { logger.LogInfo("Exiting workflow service"); } }
public TemporalTests(TemporalFixture fixture) { TestHelper.ResetDocker(this.GetType()); var settings = new TemporalSettings() { Namespace = "test-domain", TaskQueue = "test-tasks", ProxyLogLevel = LogLevel.Info, CreateNamespace = true // <-- this ensures that the default namespace exists }; // This starts/restarts the [nforgeio/temporal-dev] container for the first test // run in this class. Subsequent tests run from the class will use the existing // container instance, saving time by not having to wait for Temporal and Cassandra // to spin up and be ready for business. // // The [keepOpen=true] parameter tells the fixture to let the container continue running // after all of the tests have completed. This is useful for examining workflow histories // via the Temporal UX after the tests have completed. You can view the Temporal portal at // // http://localhost:8088 // // You can pass [keepOpen=false] to have the fixture remove the container after the // test run if you wish. if (fixture.Start(settings, reconnect: true, keepRunning: true) == TestFixtureStatus.Started) { this.fixture = fixture; this.client = fixture.Client; // Create a worker and register the workflow and activity // implementations to let Temporal know we're open for business. var worker = client.NewWorkerAsync().Result; worker.RegisterAssemblyAsync(Assembly.GetExecutingAssembly()).WaitWithoutAggregate(); worker.StartAsync().WaitWithoutAggregate(); } else { this.fixture = fixture; this.client = fixture.Client; } }
public Test_Settings(TemporalFixture fixture) { TestHelper.ResetDocker(this.GetType()); var settings = new TemporalSettings() { Namespace = TemporalFixture.Namespace, ProxyLogLevel = TemporalTestHelper.ProxyLogLevel, CreateNamespace = true, Debug = TemporalTestHelper.Debug, DebugPrelaunched = TemporalTestHelper.DebugPrelaunched, DebugDisableHeartbeats = TemporalTestHelper.DebugDisableHeartbeats, ClientIdentity = TemporalTestHelper.ClientIdentity }; this.fixture = fixture; fixture.Start(settings, composeFile: TemporalTestHelper.TemporalStackDefinition, reconnect: true, keepRunning: TemporalTestHelper.KeepTemporalServerOpen, noClient: true); }
public static async Task Main(string[] args) { // Configure the settings name such that they will be injected // into the email activity when it's constructed. // // Note that we did this before calling RegisterAssemblyAsync() below. // Dependencies added after activities have been registered will be // ignored. NeonHelper.ServiceContainer.AddSingleton(typeof(MailSettings), new MailSettings() { MailServer = "mail.my-company.com" }); // Connect to Temporal var settings = new TemporalSettings() { Namespace = "my-namespace", TaskQueue = "my-tasks", CreateNamespace = true, HostPort = "localhost:7933" }; using (var client = await TemporalClient.ConnectAsync(settings)) { // Create a worker and register the workflow and activity // implementations to let Temporal know we're open for business. var worker = await client.NewWorkerAsync(); await worker.RegisterAssemblyAsync(Assembly.GetExecutingAssembly()); await worker.StartAsync(); // Invoke the workflow. var workflowStub = client.NewWorkflowStub <IEmailWorkflow>(); await workflowStub.SendMessagesAsync(); } }
public static async Task Main(string[] args) { var settings = new TemporalSettings() { Namespace = "my-namespace", TaskQueue = "my-tasks", CreateNamespace = true, HostPort = "localhost:7933" }; using (var client = await TemporalClient.ConnectAsync(settings)) { // Create a worker and register the workflow and activity // implementations to let Temporal know we're open for business. var worker = await client.NewWorkerAsync(); await worker.RegisterAssemblyAsync(Assembly.GetExecutingAssembly()); await worker.StartAsync(); // Invoke the workflow, send it some signals and very that // it changed its state to the signal value. var stub = client.NewWorkflowStub <IMyWorkflow>(); var task = stub.DoItAsync(); await stub.SignalAsync("signal #1"); Console.WriteLine(await stub.GetStatusAsync()); await stub.SignalAsync("signal #2"); Console.WriteLine(await stub.GetStatusAsync()); // This signal completes the workflow. await stub.SignalAsync("done"); await task; } }
public Test_StubManager(TemporalFixture fixture) { TestHelper.ResetDocker(this.GetType()); var settings = new TemporalSettings() { Namespace = TemporalFixture.Namespace, ProxyLogLevel = TemporalTestHelper.ProxyLogLevel, Debug = TemporalTestHelper.Debug, ClientIdentity = TemporalTestHelper.ClientIdentity }; fixture.Start(settings, reconnect: true); this.fixture = fixture; this.client = fixture.Client; this.proxyClient = new HttpClient() { BaseAddress = client.ProxyUri }; }
public static async Task Main(string[] args) { // Connect to Temporal var settings = new TemporalSettings() { Namespace = "my-namespace", TaskQueue = "my-tasks", CreateNamespace = true, HostPort = "localhost:7933" }; using (var client = await TemporalClient.ConnectAsync(settings)) { // Create a worker and register the workflow and activity // implementations to let Temporal know we're open for business. var worker = await client.NewWorkerAsync(); await worker.RegisterWorkflowAsync <HelloWorkflow>(); await worker.StartAsync(); #region code // Invoke a workflow with options: var stub = client.NewWorkflowStub <IHelloWorkflow>( new StartWorkflowOptions() { Id = "my-ultimate-workflow", WorkflowRunTimeout = TimeSpan.FromMinutes(5) }); Console.WriteLine(await stub.HelloAsync("Jeff")); #endregion } }
public static async Task Main(string[] args) { // Connect to Temporal var settings = new TemporalSettings() { Namespace = "my-namespace", CreateNamespace = true, HostPort = "localhost:7933" }; using (var client = await TemporalClient.ConnectAsync(settings)) { var stub = client.NewWorkflowFutureStub <ICronWorkflow>( "backup", new StartWorkflowOptions() { // Run the workflow every day at 1:00am UTC: CronSchedule = "0 1 * * *" });; await stub.StartAsync(); } }
public static async Task Main(string[] args) { var settings = new TemporalSettings() { Namespace = "my-namespace", TaskQueue = "my-tasks", CreateNamespace = true, HostPort = "localhost:7933" }; using (var client = await TemporalClient.ConnectAsync(settings)) { // Create a worker and register the workflow and activity // implementations to let Temporal know we're open for business. var worker = await client.NewWorkerAsync(); await worker.RegisterAssemblyAsync(System.Reflection.Assembly.GetExecutingAssembly()); await worker.StartAsync(); // Invoke the workflow and then query it's status a few times. var stub = client.NewWorkflowStub <IMyWorkflow>(); var task = stub.DoItAsync(); for (int i = 0; i < 5; i++) { await Task.Delay(TimeSpan.FromSeconds(2.5)); Console.WriteLine(await stub.GetStatusAsync()); } await task; } }
public async Task Multiple_TaskQueues() { await SyncContext.Clear; // Test the scenario where there multiple clients without // workers that will be used to simulate apps that make calls // on workflows and then create multiple clients that register // different workflows and activities and then verify that // each of the workerless clients are able to execute workflows // and activities and that these end up being executed on the // correct clients. var clients = new List <TemporalClient>(); try { // Initialize the non-worker clients. TemporalClient client1; TemporalClient client2; TemporalClient client3; TemporalSettings settings1 = fixture.Settings.Clone(); TemporalSettings settings2 = fixture.Settings.Clone(); TemporalSettings settings3 = fixture.Settings.Clone(); settings1.TaskQueue = "taskqueue-1"; settings2.TaskQueue = "taskqueue-2"; settings3.TaskQueue = "taskqueue-3"; clients.Add(client1 = await TemporalClient.ConnectAsync(fixture.Settings)); clients.Add(client2 = await TemporalClient.ConnectAsync(fixture.Settings)); clients.Add(client3 = await TemporalClient.ConnectAsync(fixture.Settings)); // Initialize the worker clients. clients.Add(workerClient1 = await TemporalClient.ConnectAsync(settings1)); clients.Add(workerClient2 = await TemporalClient.ConnectAsync(settings2)); clients.Add(workerClient3 = await TemporalClient.ConnectAsync(settings3)); // Initialize and start the workers. var worker1 = await workerClient1.NewWorkerAsync(); await worker1.RegisterActivityAsync <ActivityWorker1>(); await worker1.RegisterWorkflowAsync <WorkflowWorker1>(); await worker1.StartAsync(); var worker2 = await workerClient2.NewWorkerAsync(); await worker2.RegisterActivityAsync <ActivityWorker2>(); await worker2.RegisterWorkflowAsync <WorkflowWorker2>(); await worker2.StartAsync(); var worker3 = await workerClient3.NewWorkerAsync(); await worker3.RegisterActivityAsync <ActivityWorker3>(); await worker3.RegisterWorkflowAsync <WorkflowWorker3>(); await worker3.StartAsync(); // Execute each of the worker workflows WITHOUT the associated activities // from each client (both the worker and non-worker clients). foreach (var client in clients) { var stub = client.NewWorkflowStub <IWorkflowWorker1>(); Assert.True(await stub.RunAsync(testActivity: false)); } foreach (var client in clients) { var stub = client.NewWorkflowStub <IWorkflowWorker2>(); Assert.True(await stub.RunAsync(testActivity: false)); } foreach (var client in clients) { var stub = client.NewWorkflowStub <IWorkflowWorker3>(); Assert.True(await stub.RunAsync(testActivity: false)); } // Re-run the workflows calling the activities this time. foreach (var client in clients) { var stub = client.NewWorkflowStub <IWorkflowWorker1>(); Assert.True(await stub.RunAsync(testActivity: true)); } foreach (var client in clients) { var stub = client.NewWorkflowStub <IWorkflowWorker2>(); Assert.True(await stub.RunAsync(testActivity: true)); } foreach (var client in clients) { var stub = client.NewWorkflowStub <IWorkflowWorker3>(); Assert.True(await stub.RunAsync(testActivity: true)); } } finally { foreach (var client in clients) { client.Dispose(); } workerClient1 = null; workerClient2 = null; workerClient3 = null; } }
/// <summary> /// Used to start the fixture within a <see cref="ComposedFixture"/>. /// </summary> /// <param name="settings">Optional Temporal settings.</param> /// <param name="name">Optionally specifies the Docker compose application name (defaults to <c>temporal-dev</c>).</param> /// <param name="composeFile"> /// <para> /// Optionally specifies the Temporal Docker compose file text. This defaults to /// <see cref="DefaultComposeFile"/> which configures Temporal server to start with /// a new Cassandra database instance listening on port <b>9042</b> as well as the /// Temporal web UI running on port <b>8088</b>. Temporal server is listening on /// its standard gRPC port <b>7233</b>. /// </para> /// <para> /// You may specify your own Docker compose text file to customize this by configuring /// a different backend database, etc. /// </para> /// </param> /// <param name="defaultNamespace">Optionally specifies the default namespace for the fixture's client. This defaults to <b>test-namespace</b>.</param> /// <param name="logLevel">Specifies the Temporal log level. This defaults to <see cref="Neon.Diagnostics.LogLevel.None"/>.</param> /// <param name="reconnect"> /// Optionally specifies that a new Temporal connection <b>should</b> be established for each /// unit test case. By default, the same connection will be reused which will save about a /// second per test. /// </param> /// <param name="keepRunning"> /// Optionally indicates that the compose application should remain running after the fixture is disposed. /// This is handy for using the Temporal web UI for port mortems after tests have completed. /// </param> /// <param name="noClient"> /// Optionally disables establishing a client connection when <c>true</c> /// is passed. The <see cref="Client"/> and <see cref="HttpClient"/> properties /// will be set to <c>null</c> in this case. /// </param> /// <param name="noReset"> /// Optionally prevents the fixture from calling <see cref="TemporalClient.Reset()"/> to /// put the Temporal client library into its initial state before the fixture starts as well /// as when the fixture itself is reset. /// </param> /// <remarks> /// <note> /// A fresh Temporal client <see cref="Client"/> will be established every time this /// fixture is started, regardless of whether the fixture has already been started. This /// ensures that each unit test will start with a client in the default state. /// </note> /// </remarks> public void StartAsComposed( TemporalSettings settings = null, string name = "temporal-dev", string composeFile = DefaultComposeFile, string defaultNamespace = Namespace, LogLevel logLevel = LogLevel.None, bool reconnect = false, bool keepRunning = false, bool noClient = false, bool noReset = false) { Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(composeFile), nameof(composeFile)); base.CheckWithinAction(); if (!IsRunning) { // The [cadence-dev] container started by [CadenceFixture] has conflicting ports // for Cassandra and the web UI, so we're going to stop that container if it's running. NeonHelper.Execute(NeonHelper.DockerCli, new object[] { "rm", "--force", new string[] { "cadence-dev_cadence_1", "cadence-dev_cadence-web_1", "cadence-dev_cassandra_1", "cadence-dev_statsd_1" } }); // Reset TemporalClient to its initial state. this.noReset = noReset; if (!noReset) { TemporalClient.Reset(); } // Start the Temporal Docker compose application. base.StartAsComposed(name, composeFile, keepRunning); // It can take Temporal server some time to start. Rather than relying on [temporal-proxy] // to handle retries (which may take longer than the connect timeout), we're going to wait // up to 4 minutes for Temporal to start listening on its RPC socket. var retry = new LinearRetryPolicy(e => true, maxAttempts: int.MaxValue, retryInterval: TimeSpan.FromSeconds(0.5), timeout: TimeSpan.FromMinutes(4)); retry.Invoke( () => { // The [socket.Connect()] calls below will throw [SocketException] until // Temporal starts listening on its RPC socket. var socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp); socket.Connect(IPAddress.IPv6Loopback, NetworkPorts.Temporal); socket.Close(); }); Thread.Sleep(TimeSpan.FromSeconds(5)); // Wait a bit longer for luck! // Initialize the settings. settings = settings ?? new TemporalSettings() { HostPort = $"localhost:{NetworkPorts.Temporal}", CreateNamespace = true, Namespace = defaultNamespace, ProxyLogLevel = logLevel, }; this.settings = settings; this.reconnect = reconnect; if (!noClient) { // Establish the Temporal connection. Client = TemporalClient.ConnectAsync(settings).Result; HttpClient = new HttpClient() { BaseAddress = Client.ListenUri }; } } }
/// <inheritdoc/> protected async override Task <int> OnRunAsync() { // Verify the environment variables. var settings = new TemporalSettings(); var hostPort = GetEnvironmentVariable("TEMPORAL_HOSTPORT"); var @namespace = GetEnvironmentVariable("TEMPORAL_NAMESPACE"); var taskQueue = GetEnvironmentVariable("TEMPORAL_TASKQUEUE"); var error = false; Log.LogInfo(() => $"TEMPORAL_HOSTPORT: {hostPort}"); Log.LogInfo(() => $"TEMPORAL_NAMESPACE: {@namespace}"); Log.LogInfo(() => $"TEMPORAL_TASKQUEUE: {taskQueue}"); if (string.IsNullOrEmpty(hostPort)) { error = true; Log.LogError("The [TEMPORAL_HOSTPORT] environment variable is required."); } settings.HostPort = hostPort; if (string.IsNullOrEmpty(@namespace)) { error = true; Log.LogError("The [TEMPORAL_NAMESPACE] environment variable is required."); } if (string.IsNullOrEmpty(taskQueue)) { error = true; Log.LogError("The [TEMPORAL_TASKQUEUE] environment variable is required."); } if (error) { return(1); } // Connect to Temporal and register the workflows and activities. Log.LogInfo("Connecting to Temporal."); settings.Namespace = @namespace; settings.TaskQueue = taskQueue; using (var client = await TemporalClient.ConnectAsync(settings)) { // Create a worker and register the workflow and activity // implementations to let Temporal know we're open for business. using (var worker = await client.NewWorkerAsync()) { Log.LogInfo("Registering workflows."); await worker.RegisterAssemblyAsync(Assembly.GetExecutingAssembly()); Log.LogInfo("Starting worker."); await worker.StartAsync(); // Indicate that the service is running. Log.LogInfo("Ready for work."); await StartedAsync(); } } // Handle termination gracefully. await Terminator.StopEvent.WaitAsync(); Terminator.ReadyToExit(); return(0); }