Skip to content

Commit 6cdf503

Browse files
committed
feat(Runner): Continued development of the Synapse Runner application
Signed-off-by: Charles d'Avernas <charles.davernas@neuroglia.io>
1 parent a0f29de commit 6cdf503

File tree

75 files changed

+1743
-515
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+1743
-515
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,3 +231,5 @@ _Pvt_Extensions
231231
# FAKE - F# Make
232232
.fake/
233233
/deployment/docker-compose/secrets/basic.json
234+
/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap/node_modules
235+
/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap/package-lock.json

Synapse.sln

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.UnitTests", "tests\
4343
EndProject
4444
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Api.Client.Core", "src\api\Synapse.Api.Client.Core\Synapse.Api.Client.Core.csproj", "{3E650D2D-B018-4056-8922-FC7DF68233B1}"
4545
EndProject
46-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Operator.Application", "src\operator\Synapse.Operator.Application\Synapse.Operator.Application.csproj", "{7627C036-B55A-479C-AB0D-C859D4A11238}"
47-
EndProject
4846
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "runtime", "runtime", "{175CE1C5-FE17-4C8B-8823-E812BAD4E527}"
4947
EndProject
5048
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Runtime.Abstractions", "src\runtime\Synapse.Runtime.Abstractions\Synapse.Runtime.Abstractions.csproj", "{A0E5E7F2-8C9C-4F36-B3FD-C09074893023}"
@@ -57,6 +55,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Runner", "src\runne
5755
EndProject
5856
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Synapse.Dashboard.StateManagement", "src\dashboard\Synapse.Dashboard.StateManagement\Synapse.Dashboard.StateManagement.csproj", "{91EF9F64-4997-407C-B353-C26B1421D0FB}"
5957
EndProject
58+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Operator", "src\operator\Synapse.Operator\Synapse.Operator.csproj", "{A9085F4A-5FDF-4F4A-B267-A03BC5E0FDB0}"
59+
EndProject
6060
Global
6161
GlobalSection(SolutionConfigurationPlatforms) = preSolution
6262
Debug|Any CPU = Debug|Any CPU
@@ -107,10 +107,6 @@ Global
107107
{3E650D2D-B018-4056-8922-FC7DF68233B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
108108
{3E650D2D-B018-4056-8922-FC7DF68233B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
109109
{3E650D2D-B018-4056-8922-FC7DF68233B1}.Release|Any CPU.Build.0 = Release|Any CPU
110-
{7627C036-B55A-479C-AB0D-C859D4A11238}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
111-
{7627C036-B55A-479C-AB0D-C859D4A11238}.Debug|Any CPU.Build.0 = Debug|Any CPU
112-
{7627C036-B55A-479C-AB0D-C859D4A11238}.Release|Any CPU.ActiveCfg = Release|Any CPU
113-
{7627C036-B55A-479C-AB0D-C859D4A11238}.Release|Any CPU.Build.0 = Release|Any CPU
114110
{A0E5E7F2-8C9C-4F36-B3FD-C09074893023}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
115111
{A0E5E7F2-8C9C-4F36-B3FD-C09074893023}.Debug|Any CPU.Build.0 = Debug|Any CPU
116112
{A0E5E7F2-8C9C-4F36-B3FD-C09074893023}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -131,6 +127,10 @@ Global
131127
{91EF9F64-4997-407C-B353-C26B1421D0FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
132128
{91EF9F64-4997-407C-B353-C26B1421D0FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
133129
{91EF9F64-4997-407C-B353-C26B1421D0FB}.Release|Any CPU.Build.0 = Release|Any CPU
130+
{A9085F4A-5FDF-4F4A-B267-A03BC5E0FDB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
131+
{A9085F4A-5FDF-4F4A-B267-A03BC5E0FDB0}.Debug|Any CPU.Build.0 = Debug|Any CPU
132+
{A9085F4A-5FDF-4F4A-B267-A03BC5E0FDB0}.Release|Any CPU.ActiveCfg = Release|Any CPU
133+
{A9085F4A-5FDF-4F4A-B267-A03BC5E0FDB0}.Release|Any CPU.Build.0 = Release|Any CPU
134134
EndGlobalSection
135135
GlobalSection(SolutionProperties) = preSolution
136136
HideSolutionNode = FALSE
@@ -153,13 +153,13 @@ Global
153153
{6F7ED286-A02C-4E78-9FE3-2689BD94B97C} = {F041A1CB-45FA-4432-BAD2-DE2EE174C6FC}
154154
{CB1DF439-BFBA-408D-BC09-3FF94E6C4F0F} = {F041A1CB-45FA-4432-BAD2-DE2EE174C6FC}
155155
{3E650D2D-B018-4056-8922-FC7DF68233B1} = {63715FC0-736D-4972-A865-41126155DF45}
156-
{7627C036-B55A-479C-AB0D-C859D4A11238} = {32EAD165-3D99-42CD-B3AF-05136DCC7F35}
157156
{175CE1C5-FE17-4C8B-8823-E812BAD4E527} = {4B9AF05C-9D6D-48C0-994D-D4A5C28FA24D}
158157
{A0E5E7F2-8C9C-4F36-B3FD-C09074893023} = {175CE1C5-FE17-4C8B-8823-E812BAD4E527}
159158
{DC24E506-602F-4FD9-B8C0-CEA6B2AD8888} = {175CE1C5-FE17-4C8B-8823-E812BAD4E527}
160159
{F327B8F1-9A13-4924-AE1B-E69788AC73E7} = {175CE1C5-FE17-4C8B-8823-E812BAD4E527}
161160
{E5FAA9BA-07C3-49CF-AD3B-897AE1D0B018} = {1DA47E5F-B23A-4D3C-96AA-4BD2662AB946}
162161
{91EF9F64-4997-407C-B353-C26B1421D0FB} = {7DF998B8-0FB1-470E-8ED0-EA1CC7B16901}
162+
{A9085F4A-5FDF-4F4A-B267-A03BC5E0FDB0} = {32EAD165-3D99-42CD-B3AF-05136DCC7F35}
163163
EndGlobalSection
164164
GlobalSection(ExtensibilityGlobals) = postSolution
165165
SolutionGuid = {2A6C03D6-355A-4B39-9F2B-D0FDE429C0E2}

src/api/Synapse.Api.Client.Core/Services/ISynapseApiClient.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ public interface ISynapseApiClient
1111
/// </summary>
1212
IClusterResourceApiClient<Namespace> Namespaces { get; }
1313

14+
/// <summary>
15+
/// Gets the Synapse API used to manage <see cref="Operator"/>s
16+
/// </summary>
17+
INamespacedResourceApiClient<Operator> Operators { get; }
18+
1419
/// <summary>
1520
/// Gets the Synapse API used to manage <see cref="Workflow"/>s
1621
/// </summary>

src/api/Synapse.Api.Client.Http/Configuration/SynapseHttpApiClientOptions.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,18 @@
66
public class SynapseHttpApiClientOptions
77
{
88

9+
/// <summary>
10+
/// Initializes a new <see cref="SynapseHttpApiClientOptions"/>
11+
/// </summary>
12+
public SynapseHttpApiClientOptions()
13+
{
14+
var uri = Environment.GetEnvironmentVariable(SynapseDefaults.EnvironmentVariables.Api.Uri);
15+
this.BaseAddress = string.IsNullOrWhiteSpace(uri) ? null! : new(uri, UriKind.RelativeOrAbsolute);
16+
}
17+
918
/// <summary>
1019
/// Gets/sets the base address of the Cloud Streams API to connect to
1120
/// </summary>
12-
public required virtual Uri BaseAddress { get; set; }
21+
public virtual Uri BaseAddress { get; set; }
1322

1423
}

src/api/Synapse.Api.Client.Http/Services/ResourceHttpApiClient.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,10 @@ public virtual async Task<ResourceDefinition> GetDefinitionAsync(CancellationTok
9696
}
9797

9898
/// <inheritdoc/>
99-
public virtual async Task<IAsyncEnumerable<TResource>> ListAsync(string @namespace, IEnumerable<LabelSelector>? labelSelectors = null, CancellationToken cancellationToken = default)
99+
public virtual async Task<IAsyncEnumerable<TResource>> ListAsync(string? @namespace = null, IEnumerable<LabelSelector>? labelSelectors = null, CancellationToken cancellationToken = default)
100100
{
101-
ArgumentException.ThrowIfNullOrWhiteSpace(@namespace);
102101
var resource = new TResource();
103-
var uri = $"/api/{resource.Definition.Version}/{resource.Definition.Plural}/{@namespace}";
102+
var uri = string.IsNullOrWhiteSpace(@namespace) ? $"/api/{resource.Definition.Version}/{resource.Definition.Plural}" : $"/api/{resource.Definition.Version}/{resource.Definition.Plural}/{@namespace}";
104103
var queryStringArguments = new Dictionary<string, string>();
105104
if (labelSelectors?.Any() == true) queryStringArguments.Add(nameof(labelSelectors), labelSelectors.Select(s => s.ToString()).Join(','));
106105
if (queryStringArguments.Count != 0) uri += $"?{queryStringArguments.Select(kvp => $"{kvp.Key}={kvp.Value}").Join('&')}";
@@ -228,7 +227,7 @@ public virtual async Task DeleteAsync(string name, string @namespace, Cancellati
228227
ArgumentException.ThrowIfNullOrWhiteSpace(name);
229228
ArgumentException.ThrowIfNullOrWhiteSpace(@namespace);
230229
var resource = new TResource();
231-
var uri = $"/api/{resource.Definition.Version}/{@namespace}/{resource.Definition.Plural}/{name}";
230+
var uri = $"/api/{resource.Definition.Version}/{resource.Definition.Plural}/{@namespace}/{name}";
232231
using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Delete, uri), cancellationToken).ConfigureAwait(false);
233232
await ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false);
234233
}

src/api/Synapse.Api.Client.Http/Services/SynapseHttpApiClient.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ public SynapseHttpApiClient(IServiceProvider serviceProvider, ILoggerFactory log
6969
/// </summary>
7070
protected HttpClient HttpClient { get; }
7171

72+
/// <inheritdoc/>
73+
public INamespacedResourceApiClient<Operator> Operators { get; private set; } = null!;
74+
7275
/// <inheritdoc/>
7376
public IClusterResourceApiClient<Namespace> Namespaces { get; private set; } = null!;
7477

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using Synapse.Resources;
2+
3+
namespace Synapse.Api.Http.Controllers;
4+
5+
/// <summary>
6+
/// Represents the <see cref="NamespacedResourceController{TResource}"/> used to manage <see cref="Operator"/>s
7+
/// </summary>
8+
/// <param name="mediator">The service used to mediate calls</param>
9+
[Route("api/v1/operators")]
10+
public class OperatorsController(IMediator mediator)
11+
: NamespacedResourceController<Operator>(mediator)
12+
{
13+
14+
15+
16+
}

src/api/Synapse.Api.Http/NamespacedResourceController.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,21 @@ public async Task<IActionResult> GetResource(string name, string @namespace, Can
3030
return this.Process(await this.Mediator.ExecuteAsync(new GetResourceQuery<TResource>(name, @namespace), cancellationToken).ConfigureAwait(false));
3131
}
3232

33+
/// <summary>
34+
/// Lists matching resources
35+
/// </summary>
36+
/// <param name="labelSelector">A comma-separated list of label selectors, if any</param>
37+
/// <param name="cancellationToken">A <see cref="CancellationToken"/></param>
38+
/// <returns>A new <see cref="IActionResult"/></returns>
39+
[HttpGet]
40+
[ProducesResponseType(typeof(Collection<Resource>), (int)HttpStatusCode.OK)]
41+
[ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))]
42+
public virtual async Task<IActionResult> GetResources(string? labelSelector = null, CancellationToken cancellationToken = default)
43+
{
44+
if (!this.TryParseLabelSelectors(labelSelector, out var labelSelectors)) return this.InvalidLabelSelector(labelSelector!);
45+
return this.Process(await this.Mediator.ExecuteAsync(new GetResourcesQuery<TResource>(null, labelSelectors), cancellationToken).ConfigureAwait(false));
46+
}
47+
3348
/// <summary>
3449
/// Lists matching resources
3550
/// </summary>
@@ -46,6 +61,23 @@ public virtual async Task<IActionResult> GetResources(string @namespace, string?
4661
return this.Process(await this.Mediator.ExecuteAsync(new GetResourcesQuery<TResource>(@namespace, labelSelectors), cancellationToken).ConfigureAwait(false));
4762
}
4863

64+
/// <summary>
65+
/// Lists matching resources
66+
/// </summary>
67+
/// <param name="labelSelector">A comma-separated list of label selectors, if any</param>
68+
/// <param name="maxResults">The maximum amount, if any, of results to list at once</param>
69+
/// <param name="continuationToken">A token, defined by a previously retrieved collection, used to continue enumerating through matches</param>
70+
/// <param name="cancellationToken">A <see cref="CancellationToken"/></param>
71+
/// <returns>A new <see cref="IActionResult"/></returns>
72+
[HttpGet("list")]
73+
[ProducesResponseType(typeof(Collection<Resource>), (int)HttpStatusCode.OK)]
74+
[ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))]
75+
public virtual async Task<IActionResult> ListResources(string? labelSelector = null, ulong? maxResults = null, string? continuationToken = null, CancellationToken cancellationToken = default)
76+
{
77+
if (!this.TryParseLabelSelectors(labelSelector, out var labelSelectors)) return this.InvalidLabelSelector(labelSelector!);
78+
return this.Process(await this.Mediator.ExecuteAsync(new ListResourcesQuery<TResource>(null, labelSelectors, maxResults, continuationToken), cancellationToken).ConfigureAwait(false));
79+
}
80+
4981
/// <summary>
5082
/// Lists matching resources
5183
/// </summary>

src/api/Synapse.Api.Http/Services/ResourceWatchEventHubController.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public virtual async Task WatchResourcesAsync(string connectionId, ResourceDefin
8686
});
8787
}
8888
watch = await this.Resources.WatchAsync(definition.Group, definition.Version, definition.Plural, @namespace, cancellationToken: cancellationToken).ConfigureAwait(false);
89-
watch.SubscribeAsync(e => this.OnResourceWatchEventAsync(connectionId, e));
89+
watch.SubscribeAsync(e => this.OnResourceWatchEventAsync(connectionId, e), this.CancellationTokenSource.Token);
9090
subscriptions.AddOrUpdate(subscriptionKey, watch, (key, current) =>
9191
{
9292
current.Dispose();

src/core/Synapse.Core.Infrastructure.Containers.Docker/DockerContainer.cs

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ public class DockerContainer(string id, IDockerClient dockerClient)
2424
: IContainer
2525
{
2626

27+
bool _disposed;
28+
2729
/// <summary>
2830
/// Gets the container's ID
2931
/// </summary>
@@ -35,15 +37,17 @@ public class DockerContainer(string id, IDockerClient dockerClient)
3537
protected virtual IDockerClient DockerClient { get; } = dockerClient;
3638

3739
/// <inheritdoc/>
38-
public virtual StreamReader? StandardOutput { get; set; }
40+
public virtual StreamReader? StandardOutput { get; protected set; }
41+
42+
/// <inheritdoc/>
43+
public virtual StreamReader? StandardError { get; protected set; }
3944

4045
/// <inheritdoc/>
41-
public virtual StreamReader? StandardError { get; set; }
46+
public long? ExitCode { get; protected set; }
4247

4348
/// <inheritdoc/>
4449
public virtual async Task StartAsync(CancellationToken cancellationToken = default)
4550
{
46-
4751
await this.DockerClient.Containers.StartContainerAsync(this.Id, new() { }, cancellationToken).ConfigureAwait(false);
4852
#pragma warning disable CS0618 // Type or member is obsolete
4953
var standardOutputStream = await this.DockerClient.Containers.GetContainerLogsAsync(this.Id, new() { Follow = true, ShowStdout = true, ShowStderr = true, Timestamps = false }, cancellationToken).ConfigureAwait(false);
@@ -54,9 +58,49 @@ public virtual async Task StartAsync(CancellationToken cancellationToken = defau
5458
}
5559

5660
/// <inheritdoc/>
57-
public virtual Task WaitForExitAsync(CancellationToken cancellationToken = default) => this.DockerClient.Containers.WaitContainerAsync(this.Id, cancellationToken);
61+
public virtual async Task WaitForExitAsync(CancellationToken cancellationToken = default)
62+
{
63+
var response = await this.DockerClient.Containers.WaitContainerAsync(this.Id, cancellationToken).ConfigureAwait(false);
64+
this.ExitCode = response.StatusCode;
65+
}
5866

5967
/// <inheritdoc/>
6068
public virtual Task StopAsync(CancellationToken cancellationToken = default) => this.DockerClient.Containers.StopContainerAsync(this.Id, new() { }, cancellationToken);
6169

70+
/// <summary>
71+
/// Disposes of the <see cref="DockerContainer"/>
72+
/// </summary>
73+
/// <param name="disposing">A boolean indicating whether or not the <see cref="DockerContainer"/> is being disposed of</param>
74+
/// <returns>A new awaitable <see cref="ValueTask"/></returns>
75+
protected virtual async ValueTask DisposeAsync(bool disposing)
76+
{
77+
if (!this._disposed) return;
78+
this._disposed = true;
79+
await Task.CompletedTask.ConfigureAwait(false);
80+
}
81+
82+
/// <inheritdoc/>
83+
public async ValueTask DisposeAsync()
84+
{
85+
await this.DisposeAsync(true).ConfigureAwait(false);
86+
GC.SuppressFinalize(this);
87+
}
88+
89+
/// <summary>
90+
/// Disposes of the <see cref="DockerContainer"/>
91+
/// </summary>
92+
/// <param name="disposing">A boolean indicating whether or not the <see cref="DockerContainer"/> is being disposed of</param>
93+
protected virtual void Dispose(bool disposing)
94+
{
95+
if (!this._disposed) return;
96+
this._disposed = true;
97+
}
98+
99+
/// <inheritdoc/>
100+
public void Dispose()
101+
{
102+
this.Dispose(true);
103+
GC.SuppressFinalize(this);
104+
}
105+
62106
}

src/core/Synapse.Core.Infrastructure/Extensions/IServiceCollectionExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ public static IServiceCollection AddSynapse(this IServiceCollection services, IC
3939
services.AddPluginProvider();
4040

4141
var redisConnectionString = configuration.GetConnectionString(RedisDatabase.ConnectionStringName);
42-
services.AddPlugin(typeof(IDatabase), string.IsNullOrWhiteSpace(redisConnectionString) ? null : provider => provider.GetRequiredService<RedisDatabase>(), serviceLifetime: ServiceLifetime.Scoped);
42+
services.AddPlugin(typeof(IDatabase), string.IsNullOrWhiteSpace(redisConnectionString) ? null : provider => provider.GetRequiredService<RedisDatabase>(), serviceLifetime: ServiceLifetime.Singleton);
4343

44-
if (!string.IsNullOrWhiteSpace(redisConnectionString)) services.AddRedisDatabase(redisConnectionString, ServiceLifetime.Scoped);
44+
if (!string.IsNullOrWhiteSpace(redisConnectionString)) services.AddRedisDatabase(redisConnectionString, ServiceLifetime.Singleton);
4545
services.AddHostedService<Core.Infrastructure.Services.DatabaseInitializer>();
4646

4747
services.AddPlugin(typeof(IRepository<Document>), provider => provider.GetRequiredService<MongoRepository<Document, string>>(), serviceLifetime: ServiceLifetime.Scoped);

src/core/Synapse.Core.Infrastructure/Services/Interfaces/IContainer.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ namespace Synapse.Core.Infrastructure.Services;
1717
/// Defines the fundamentals of a container
1818
/// </summary>
1919
public interface IContainer
20+
: IDisposable, IAsyncDisposable
2021
{
2122

2223
/// <summary>
@@ -29,6 +30,11 @@ public interface IContainer
2930
/// </summary>
3031
StreamReader? StandardError { get; }
3132

33+
/// <summary>
34+
/// Gets the container's exit code
35+
/// </summary>
36+
long? ExitCode { get; }
37+
3238
/// <summary>
3339
/// Starts the container
3440
/// </summary>

src/core/Synapse.Core.Infrastructure/Services/Interfaces/IContainerPlatform.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
namespace Synapse.Core.Infrastructure.Services;
1717

1818
/// <summary>
19-
/// Defines the fundamentals of a service used to interact with a container platform
19+
/// Defines the fundamentals of a service used to manage containers
2020
/// </summary>
2121
public interface IContainerPlatform
2222
{
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
namespace Synapse;
2+
3+
/// <summary>
4+
/// Enumerates all default operator runtime modes
5+
/// </summary>
6+
public static class OperatorRuntimeMode
7+
{
8+
9+
/// <summary>
10+
/// Gets the native operator runtime mode
11+
/// </summary>
12+
public const string Native = "native";
13+
/// <summary>
14+
/// Gets the containerized operator runtime mode
15+
/// </summary>
16+
public const string Containerized = "containerized";
17+
18+
/// <summary>
19+
/// Gets a new <see cref="IEnumerable{T}"/> containing all default operator runtime modes
20+
/// </summary>
21+
/// <returns>A new <see cref="IEnumerable{T}"/> containing all default operator runtime modes</returns>
22+
public static IEnumerable<string> AsEnumerable()
23+
{
24+
yield return Native;
25+
yield return Containerized;
26+
}
27+
28+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright © 2024-Present Neuroglia SRL. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"),
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
namespace Synapse;
15+
16+
/// <summary>
17+
/// Exposes all default operator status phases
18+
/// </summary>
19+
public static class OperatorStatusPhase
20+
{
21+
22+
/// <summary>
23+
/// Indicates that the operator is running
24+
/// </summary>
25+
public const string Running = "running";
26+
/// <summary>
27+
/// Indicates that the operator has been stopped and is not running
28+
/// </summary>
29+
public const string Stopped = "stopped";
30+
31+
/// <summary>
32+
/// Gets an <see cref="IEnumerable{T}"/> containing default operator status phases
33+
/// </summary>
34+
/// <returns>A new <see cref="IEnumerable{T}"/> containing default operator status phases</returns>
35+
public static IEnumerable<string> AsEnumerable()
36+
{
37+
yield return Running;
38+
yield return Stopped;
39+
}
40+
41+
}

0 commit comments

Comments
 (0)