diff --git a/Synapse.sln b/Synapse.sln
index cc15684e6..d9e4093d0 100644
--- a/Synapse.sln
+++ b/Synapse.sln
@@ -128,12 +128,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "templates", "templates", "{
deployments\helm\templates\services.yaml = deployments\helm\templates\services.yaml
EndProjectSection
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Synapse.Runtime.Docker", "src\runtime\Synapse.Runtime.Docker\Synapse.Runtime.Docker.csproj", "{8FF58403-9E13-4F58-864F-E6FBC877BF37}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Runtime.Docker", "src\runtime\Synapse.Runtime.Docker\Synapse.Runtime.Docker.csproj", "{8FF58403-9E13-4F58-864F-E6FBC877BF37}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Synapse.Runtime.Kubernetes", "src\runtime\Synapse.Runtime.Kubernetes\Synapse.Runtime.Kubernetes.csproj", "{9B37AA4A-A342-4A41-A2A1-C8466825A70A}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Runtime.Kubernetes", "src\runtime\Synapse.Runtime.Kubernetes\Synapse.Runtime.Kubernetes.csproj", "{9B37AA4A-A342-4A41-A2A1-C8466825A70A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Core.Infrastructure.Containers.Docker", "src\core\Synapse.Core.Infrastructure.Containers.Docker\Synapse.Core.Infrastructure.Containers.Docker.csproj", "{DD6381BD-2C8B-4CE1-99B2-EC585DD818FA}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "kubernetes", "kubernetes", "{B3F3DB1B-23E7-45FA-8934-448BFFB294E8}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Synapse.Core.Infrastructure.Containers.Kubernetes", "src\core\Synapse.Core.Infrastructure.Containers.Kubernetes\Synapse.Core.Infrastructure.Containers.Kubernetes.csproj", "{41C99069-BD99-4FD2-BF33-984CF03B53E8}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -224,6 +228,10 @@ Global
{DD6381BD-2C8B-4CE1-99B2-EC585DD818FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DD6381BD-2C8B-4CE1-99B2-EC585DD818FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DD6381BD-2C8B-4CE1-99B2-EC585DD818FA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {41C99069-BD99-4FD2-BF33-984CF03B53E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {41C99069-BD99-4FD2-BF33-984CF03B53E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {41C99069-BD99-4FD2-BF33-984CF03B53E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {41C99069-BD99-4FD2-BF33-984CF03B53E8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -262,6 +270,8 @@ Global
{8FF58403-9E13-4F58-864F-E6FBC877BF37} = {175CE1C5-FE17-4C8B-8823-E812BAD4E527}
{9B37AA4A-A342-4A41-A2A1-C8466825A70A} = {175CE1C5-FE17-4C8B-8823-E812BAD4E527}
{DD6381BD-2C8B-4CE1-99B2-EC585DD818FA} = {9E296C8A-4D78-4592-B046-11A3A953FD25}
+ {B3F3DB1B-23E7-45FA-8934-448BFFB294E8} = {562C91A3-6E91-4489-9D9D-064E7436D900}
+ {41C99069-BD99-4FD2-BF33-984CF03B53E8} = {9E296C8A-4D78-4592-B046-11A3A953FD25}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2A6C03D6-355A-4B39-9F2B-D0FDE429C0E2}
diff --git a/deployments/helm/templates/deployment.yaml b/deployments/helm/templates/deployment.yaml
index 3395d97f0..889e5c5f8 100644
--- a/deployments/helm/templates/deployment.yaml
+++ b/deployments/helm/templates/deployment.yaml
@@ -88,9 +88,13 @@ spec:
- name: CONNECTIONSTRINGS__REDIS
value: {{ .Values.global.redisConnectionString }}
- name: SYNAPSE_OPERATOR_NAMESPACE
- value: {{ .Values.operator.env.SYNAPSE_OPERATOR_NAMESPACE }}
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.namespace
- name: SYNAPSE_OPERATOR_NAME
- value: {{ .Values.operator.env.SYNAPSE_OPERATOR_NAME }}
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.name
- name: SYNAPSE_OPERATOR_RUNNER_API
value: {{ .Values.operator.env.SYNAPSE_OPERATOR_RUNNER_API }}
- name: DOCKER_HOST
@@ -128,9 +132,13 @@ spec:
- name: CONNECTIONSTRINGS__REDIS
value: {{ .Values.global.redisConnectionString }}
- name: SYNAPSE_CORRELATOR_NAMESPACE
- value: {{ .Values.correlator.env.SYNAPSE_CORRELATOR_NAMESPACE }}
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.namespace
- name: SYNAPSE_CORRELATOR_NAME
- value: {{ .Values.correlator.env.SYNAPSE_CORRELATOR_NAME }}
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.name
---
apiVersion: v1
kind: PersistentVolumeClaim
diff --git a/deployments/helm/values.yaml b/deployments/helm/values.yaml
index 458c43c56..508b2f018 100644
--- a/deployments/helm/values.yaml
+++ b/deployments/helm/values.yaml
@@ -33,8 +33,6 @@ operator:
service:
type: ClusterIP
env:
- SYNAPSE_OPERATOR_NAMESPACE: default
- SYNAPSE_OPERATOR_NAME: operator-1
SYNAPSE_OPERATOR_RUNNER_API: http://api:8080
DOCKER_HOST: unix:///var/run/docker.sock
@@ -45,9 +43,6 @@ correlator:
service:
type: ClusterIP
port: 8081
- env:
- SYNAPSE_CORRELATOR_NAMESPACE: default
- SYNAPSE_CORRELATOR_NAME: correlator-1
serviceAccount:
create: true
diff --git a/deployments/kubernetes/api.yaml b/deployments/kubernetes/api.yaml
new file mode 100644
index 000000000..139fa3294
--- /dev/null
+++ b/deployments/kubernetes/api.yaml
@@ -0,0 +1,53 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: api-1
+ namespace: synapse
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: api
+ template:
+ metadata:
+ labels:
+ app: api
+ spec:
+ containers:
+ - name: api
+ image: ghcr.io/serverlessworkflow/synapse/api:1.0.0-alpha1
+ env:
+ - name: CONNECTIONSTRINGS__REDIS
+ value: garnet:6379
+ - name: SYNAPSE_DASHBOARD_SERVE
+ value: 'true'
+ - name: SYNAPSE_API_AUTH_TOKEN_FILE
+ value: /app/tokens.yaml
+ - name: SYNAPSE_API_JWT_AUTHORITY
+ value: http://api:8080
+ - name: SYNAPSE_API_CLOUD_EVENTS_ENDPOINT
+ value: https://webhook.site/a4aff725-0711-48b2-a9d2-5d1b806d04d0
+ ports:
+ - containerPort: 8080
+ volumeMounts:
+ - name: tokens
+ mountPath: /app/tokens.yaml
+ subPath: tokens.yaml
+ volumes:
+ - name: tokens
+ hostPath:
+ path: /run/desktop/mnt/host/c/Users/User/.synapse
+---
+
+apiVersion: v1
+kind: Service
+metadata:
+ name: api
+ namespace: synapse
+spec:
+ ports:
+ - port: 8080
+ targetPort: 8080
+ selector:
+ app: api
+ type: ClusterIP
diff --git a/deployments/kubernetes/correlator.yaml b/deployments/kubernetes/correlator.yaml
new file mode 100644
index 000000000..70a1d1390
--- /dev/null
+++ b/deployments/kubernetes/correlator.yaml
@@ -0,0 +1,42 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: correlator-1
+ namespace: synapse
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: correlator
+ template:
+ metadata:
+ labels:
+ app: correlator
+ spec:
+ containers:
+ - name: correlator
+ image: ghcr.io/serverlessworkflow/synapse/correlator:1.0.0-alpha1
+ env:
+ - name: CONNECTIONSTRINGS__REDIS
+ value: garnet:6379
+ - name: SYNAPSE_CORRELATOR_NAMESPACE
+ value: default
+ - name: SYNAPSE_CORRELATOR_NAME
+ value: correlator-1
+ ports:
+ - containerPort: 8080
+
+---
+
+apiVersion: v1
+kind: Service
+metadata:
+ name: correlator
+ namespace: synapse
+spec:
+ ports:
+ - port: 8080
+ targetPort: 8080
+ selector:
+ app: correlator
+ type: ClusterIP
diff --git a/deployments/kubernetes/garnet.yaml b/deployments/kubernetes/garnet.yaml
new file mode 100644
index 000000000..982653e24
--- /dev/null
+++ b/deployments/kubernetes/garnet.yaml
@@ -0,0 +1,35 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: garnet
+ namespace: synapse
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: garnet
+ template:
+ metadata:
+ labels:
+ app: garnet
+ spec:
+ containers:
+ - name: garnet
+ image: ghcr.io/microsoft/garnet
+ ports:
+ - containerPort: 6379
+
+---
+
+apiVersion: v1
+kind: Service
+metadata:
+ name: garnet
+ namespace: synapse
+spec:
+ ports:
+ - port: 6379
+ targetPort: 6379
+ selector:
+ app: garnet
+ type: ClusterIP
\ No newline at end of file
diff --git a/deployments/kubernetes/namespace.yaml b/deployments/kubernetes/namespace.yaml
new file mode 100644
index 000000000..17c883dbb
--- /dev/null
+++ b/deployments/kubernetes/namespace.yaml
@@ -0,0 +1,4 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: synapse
\ No newline at end of file
diff --git a/deployments/kubernetes/operator.yaml b/deployments/kubernetes/operator.yaml
new file mode 100644
index 000000000..7b45ad34f
--- /dev/null
+++ b/deployments/kubernetes/operator.yaml
@@ -0,0 +1,86 @@
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: operator
+ namespace: synapse
+
+---
+
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: operator-role
+rules:
+- apiGroups: [""]
+ resources: ["pods"]
+ verbs: ["get", "list", "watch", "create", "update", "delete"]
+
+---
+
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: operator-role-binding
+subjects:
+- kind: ServiceAccount
+ name: operator
+ namespace: synapse
+roleRef:
+ kind: ClusterRole
+ name: operator-role
+ apiGroup: rbac.authorization.k8s.io
+
+---
+
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: operator-1
+ namespace: synapse
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: operator
+ template:
+ metadata:
+ labels:
+ app: operator
+ spec:
+ serviceAccountName: operator
+ containers:
+ - name: operator
+ image: ghcr.io/serverlessworkflow/synapse/operator:1.0.0-alpha1
+ env:
+ - name: CONNECTIONSTRINGS__REDIS
+ value: garnet:6379
+ - name: SYNAPSE_OPERATOR_NAMESPACE
+ value: default
+ - name: SYNAPSE_OPERATOR_NAME
+ value: operator-1
+ - name: SYNAPSE_RUNNER_API
+ value: http://api:8080
+ - name: SYNAPSE_RUNNER_LIFECYCLE_EVENTS
+ value: "true"
+ - name: SYNAPSE_RUNNER_CONTAINER_PLATFORM
+ value: kubernetes
+ - name: SYNAPSE_RUNTIME_MODE
+ value: kubernetes
+ - name: SYNAPSE_RUNTIME_K8S_SERVICE_ACCOUNT
+ value: operator
+ - name: SYNAPSE_RUNTIME_K8S_NAMESPACE
+ value: synapse
+---
+
+apiVersion: v1
+kind: Service
+metadata:
+ name: operator
+ namespace: synapse
+spec:
+ ports:
+ - port: 80
+ targetPort: 8080
+ selector:
+ app: operator
+ type: ClusterIP
diff --git a/src/api/Synapse.Api.Application/Synapse.Api.Application.csproj b/src/api/Synapse.Api.Application/Synapse.Api.Application.csproj
index fac18d996..ddff43f0a 100644
--- a/src/api/Synapse.Api.Application/Synapse.Api.Application.csproj
+++ b/src/api/Synapse.Api.Application/Synapse.Api.Application.csproj
@@ -24,6 +24,7 @@
transparent_logomark_256.png
README.md
Contains the Synapse API commands, queries and services
+ $(VersionPrefix)-$(VersionSuffix)
$(VersionPrefix).0
$(VersionPrefix).0
embedded
diff --git a/src/api/Synapse.Api.Client.Core/Synapse.Api.Client.Core.csproj b/src/api/Synapse.Api.Client.Core/Synapse.Api.Client.Core.csproj
index 36da5a8a6..323ad898a 100644
--- a/src/api/Synapse.Api.Client.Core/Synapse.Api.Client.Core.csproj
+++ b/src/api/Synapse.Api.Client.Core/Synapse.Api.Client.Core.csproj
@@ -24,6 +24,7 @@
transparent_logomark_256.png
README.md
Contains abstractions and interfaces used by clients of the Synapse API
+ $(VersionPrefix)-$(VersionSuffix)
$(VersionPrefix).0
$(VersionPrefix).0
embedded
diff --git a/src/api/Synapse.Api.Client.Http/Synapse.Api.Client.Http.csproj b/src/api/Synapse.Api.Client.Http/Synapse.Api.Client.Http.csproj
index 30a4bb148..f25c122ed 100644
--- a/src/api/Synapse.Api.Client.Http/Synapse.Api.Client.Http.csproj
+++ b/src/api/Synapse.Api.Client.Http/Synapse.Api.Client.Http.csproj
@@ -24,6 +24,7 @@
transparent_logomark_256.png
README.md
Contains the HTTP client for the Synapse API
+ $(VersionPrefix)-$(VersionSuffix)
$(VersionPrefix).0
$(VersionPrefix).0
embedded
diff --git a/src/api/Synapse.Api.Http/Synapse.Api.Http.csproj b/src/api/Synapse.Api.Http/Synapse.Api.Http.csproj
index 189b0613b..cecfd3097 100644
--- a/src/api/Synapse.Api.Http/Synapse.Api.Http.csproj
+++ b/src/api/Synapse.Api.Http/Synapse.Api.Http.csproj
@@ -25,6 +25,7 @@
transparent_logomark_256.png
README.md
Contains the services and controllers used by Synapse HTTP API
+ $(VersionPrefix)-$(VersionSuffix)
$(VersionPrefix).0
$(VersionPrefix).0
embedded
diff --git a/src/api/Synapse.Api.Server/Synapse.Api.Server.csproj b/src/api/Synapse.Api.Server/Synapse.Api.Server.csproj
index 35cadae3c..09eabe504 100644
--- a/src/api/Synapse.Api.Server/Synapse.Api.Server.csproj
+++ b/src/api/Synapse.Api.Server/Synapse.Api.Server.csproj
@@ -19,6 +19,7 @@
synapse api server
Apache-2.0
True
+ $(VersionPrefix)-$(VersionSuffix)
$(VersionPrefix).0
$(VersionPrefix).0
embedded
diff --git a/src/cli/Synapse.Cli/Synapse.Cli.csproj b/src/cli/Synapse.Cli/Synapse.Cli.csproj
index e0bce2918..2283ecb91 100644
--- a/src/cli/Synapse.Cli/Synapse.Cli.csproj
+++ b/src/cli/Synapse.Cli/Synapse.Cli.csproj
@@ -21,6 +21,7 @@
en
Apache-2.0
True
+ $(VersionPrefix)-$(VersionSuffix)
$(VersionPrefix).0
$(VersionPrefix).0
embedded
diff --git a/src/core/Synapse.Core.Infrastructure.Containers.Docker/DockerContainer.cs b/src/core/Synapse.Core.Infrastructure.Containers.Docker/DockerContainer.cs
index 74988c85f..c2fc5b107 100644
--- a/src/core/Synapse.Core.Infrastructure.Containers.Docker/DockerContainer.cs
+++ b/src/core/Synapse.Core.Infrastructure.Containers.Docker/DockerContainer.cs
@@ -19,8 +19,8 @@ namespace Synapse.Core.Infrastructure.Containers;
/// Represents a Docker
///
/// The container's ID
-/// The service used to interact with the Docker API
-public class DockerContainer(string id, IDockerClient dockerClient)
+/// The service used to interact with the Docker API
+public class DockerContainer(string id, IDockerClient docker)
: IContainer
{
@@ -34,7 +34,7 @@ public class DockerContainer(string id, IDockerClient dockerClient)
///
/// Gets the service used to interact with the Docker API
///
- protected virtual IDockerClient DockerClient { get; } = dockerClient;
+ protected virtual IDockerClient Docker { get; } = docker;
///
public virtual StreamReader? StandardOutput { get; protected set; }
@@ -48,10 +48,10 @@ public class DockerContainer(string id, IDockerClient dockerClient)
///
public virtual async Task StartAsync(CancellationToken cancellationToken = default)
{
- await this.DockerClient.Containers.StartContainerAsync(this.Id, new() { }, cancellationToken).ConfigureAwait(false);
+ await this.Docker.Containers.StartContainerAsync(this.Id, new() { }, cancellationToken).ConfigureAwait(false);
#pragma warning disable CS0618 // Type or member is obsolete
- var standardOutputStream = await this.DockerClient.Containers.GetContainerLogsAsync(this.Id, new() { Follow = true, ShowStdout = true, ShowStderr = true, Timestamps = false }, cancellationToken).ConfigureAwait(false);
- var standardErrorStream = await this.DockerClient.Containers.GetContainerLogsAsync(this.Id, new() { Follow = true, ShowStdout = false, ShowStderr = true, Timestamps = false }, cancellationToken).ConfigureAwait(false);
+ var standardOutputStream = await this.Docker.Containers.GetContainerLogsAsync(this.Id, new() { Follow = true, ShowStdout = true, ShowStderr = true, Timestamps = false }, cancellationToken).ConfigureAwait(false);
+ var standardErrorStream = await this.Docker.Containers.GetContainerLogsAsync(this.Id, new() { Follow = true, ShowStdout = false, ShowStderr = true, Timestamps = false }, cancellationToken).ConfigureAwait(false);
#pragma warning restore CS0618 // Type or member is obsolete
this.StandardOutput = new(standardOutputStream);
this.StandardError = new(standardErrorStream);
@@ -60,12 +60,12 @@ public virtual async Task StartAsync(CancellationToken cancellationToken = defau
///
public virtual async Task WaitForExitAsync(CancellationToken cancellationToken = default)
{
- var response = await this.DockerClient.Containers.WaitContainerAsync(this.Id, cancellationToken).ConfigureAwait(false);
+ var response = await this.Docker.Containers.WaitContainerAsync(this.Id, cancellationToken).ConfigureAwait(false);
this.ExitCode = response.StatusCode;
}
///
- public virtual Task StopAsync(CancellationToken cancellationToken = default) => this.DockerClient.Containers.StopContainerAsync(this.Id, new() { }, cancellationToken);
+ public virtual Task StopAsync(CancellationToken cancellationToken = default) => this.Docker.Containers.StopContainerAsync(this.Id, new() { }, cancellationToken);
///
/// Disposes of the
diff --git a/src/core/Synapse.Core.Infrastructure.Containers.Docker/Extensions/DockerContainerServiceCollectionExtensions.cs b/src/core/Synapse.Core.Infrastructure.Containers.Docker/Extensions/DockerContainerPlatformServiceCollectionExtensions.cs
similarity index 95%
rename from src/core/Synapse.Core.Infrastructure.Containers.Docker/Extensions/DockerContainerServiceCollectionExtensions.cs
rename to src/core/Synapse.Core.Infrastructure.Containers.Docker/Extensions/DockerContainerPlatformServiceCollectionExtensions.cs
index b7f19a747..20173b26e 100644
--- a/src/core/Synapse.Core.Infrastructure.Containers.Docker/Extensions/DockerContainerServiceCollectionExtensions.cs
+++ b/src/core/Synapse.Core.Infrastructure.Containers.Docker/Extensions/DockerContainerPlatformServiceCollectionExtensions.cs
@@ -21,7 +21,7 @@ namespace Synapse.Core.Infrastructure.Containers;
///
/// Defines extensions for s
///
-public static class DockerContainerServiceCollectionExtensions
+public static class DockerContainerPlatformServiceCollectionExtensions
{
///
diff --git a/src/core/Synapse.Core.Infrastructure.Containers.Docker/Synapse.Core.Infrastructure.Containers.Docker.csproj b/src/core/Synapse.Core.Infrastructure.Containers.Docker/Synapse.Core.Infrastructure.Containers.Docker.csproj
index 7c30879ee..4d42b6998 100644
--- a/src/core/Synapse.Core.Infrastructure.Containers.Docker/Synapse.Core.Infrastructure.Containers.Docker.csproj
+++ b/src/core/Synapse.Core.Infrastructure.Containers.Docker/Synapse.Core.Infrastructure.Containers.Docker.csproj
@@ -25,6 +25,7 @@
transparent_logomark_256.png
README.md
Contains the Docker container platform for running Synapse in a containerized environment.
+ $(VersionPrefix)-$(VersionSuffix)
$(VersionPrefix).0
$(VersionPrefix).0
embedded
@@ -46,7 +47,6 @@
-
diff --git a/src/core/Synapse.Core.Infrastructure.Containers.Kubernetes/Configuration/KubernetesContainerPlatformOptions.cs b/src/core/Synapse.Core.Infrastructure.Containers.Kubernetes/Configuration/KubernetesContainerPlatformOptions.cs
new file mode 100644
index 000000000..0c3833a6a
--- /dev/null
+++ b/src/core/Synapse.Core.Infrastructure.Containers.Kubernetes/Configuration/KubernetesContainerPlatformOptions.cs
@@ -0,0 +1,38 @@
+// Copyright © 2024-Present The Synapse 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.
+
+using System.Runtime.Serialization;
+using System.Text.Json.Serialization;
+using YamlDotNet.Serialization;
+
+namespace Synapse.Core.Infrastructure.Containers.Configuration;
+
+///
+/// Represents the options used to configure the
+///
+public class KubernetesContainerPlatformOptions
+{
+
+ ///
+ /// Gets/sets the path to the Kubeconfig file to use, if any. If not set, defaults to 'InCluster' configuration
+ ///
+ [DataMember(Order = 1, Name = "kubeconfig"), JsonPropertyOrder(1), JsonPropertyName("kubeconfig"), YamlMember(Order = 1, Alias = "kubeconfig")]
+ public virtual string? Kubeconfig { get; set; }
+
+ ///
+ /// Gets/sets the Kubernetes image pull policy. Supported values are 'Always', 'IfNotPresent' and 'Never'. Defaults to 'Always'.
+ ///
+ [DataMember(Order = 2, Name = "imagePullPolicy"), JsonPropertyOrder(2), JsonPropertyName("imagePullPolicy"), YamlMember(Order = 2, Alias = "imagePullPolicy")]
+ public virtual string ImagePullPolicy { get; set; } = Synapse.ImagePullPolicy.Always;
+
+}
diff --git a/src/core/Synapse.Core.Infrastructure.Containers.Kubernetes/Extensions/KubernetesContainerPlatformServiceCollectionExtensions.cs b/src/core/Synapse.Core.Infrastructure.Containers.Kubernetes/Extensions/KubernetesContainerPlatformServiceCollectionExtensions.cs
new file mode 100644
index 000000000..651bf2152
--- /dev/null
+++ b/src/core/Synapse.Core.Infrastructure.Containers.Kubernetes/Extensions/KubernetesContainerPlatformServiceCollectionExtensions.cs
@@ -0,0 +1,40 @@
+// Copyright © 2024-Present The Synapse 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.
+
+using Synapse.Core.Infrastructure.Services;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.Hosting;
+
+namespace Synapse.Core.Infrastructure.Containers;
+
+///
+/// Defines extensions for s
+///
+public static class KubernetesContainerPlatformServiceCollectionExtensions
+{
+
+ ///
+ /// Adds and configures a new
+ ///
+ /// The to configure
+ /// The configured
+ public static IServiceCollection AddKubernetesContainerPlatform(this IServiceCollection services)
+ {
+ services.TryAddSingleton();
+ services.AddSingleton(provider => provider.GetRequiredService());
+ services.AddSingleton(provider => provider.GetRequiredService());
+ return services;
+ }
+
+}
diff --git a/src/core/Synapse.Core.Infrastructure.Containers.Kubernetes/KubernetesContainer.cs b/src/core/Synapse.Core.Infrastructure.Containers.Kubernetes/KubernetesContainer.cs
new file mode 100644
index 000000000..09284d920
--- /dev/null
+++ b/src/core/Synapse.Core.Infrastructure.Containers.Kubernetes/KubernetesContainer.cs
@@ -0,0 +1,167 @@
+// Copyright © 2024-Present The Synapse 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.
+
+using k8s;
+using k8s.Models;
+using Microsoft.Extensions.Logging;
+using Synapse.Core.Infrastructure.Services;
+
+namespace Synapse.Core.Infrastructure.Containers;
+
+///
+/// Represents a Kubernetes
+///
+/// The the belongs to
+/// The service used to perform logging
+/// The service used to interact with the Docker API
+public class KubernetesContainer(V1Pod pod, ILogger logger, IKubernetes kubernetes)
+ : IContainer
+{
+
+ bool _disposed;
+
+ ///
+ /// Gets the the container belongs to
+ ///
+ protected V1Pod Pod { get; set; } = pod;
+
+ ///
+ /// Gets the service used to perform logging
+ ///
+ protected ILogger Logger { get; } = logger;
+
+ ///
+ /// Gets the service used to interact with the Docker API
+ ///
+ protected virtual IKubernetes Kubernetes { get; } = kubernetes;
+
+ ///
+ public virtual StreamReader? StandardOutput { get; protected set; }
+
+ ///
+ public virtual StreamReader? StandardError { get; protected set; }
+
+ ///
+ public long? ExitCode { get; protected set; }
+
+ ///
+ /// Gets the 's
+ ///
+ protected CancellationTokenSource CancellationTokenSource { get; } = new();
+
+ ///
+ public virtual async Task StartAsync(CancellationToken cancellationToken = default)
+ {
+ try
+ {
+ this.Logger.LogDebug("Creating pod '{pod}'...", $"{this.Pod.Name()}.{this.Pod.Namespace()}");
+ this.Pod = await this.Kubernetes.CoreV1.CreateNamespacedPodAsync(this.Pod, this.Pod.Namespace(), cancellationToken: cancellationToken);
+ this.Logger.LogDebug("The pod '{pod}' has been successfully created", $"{this.Pod.Name()}.{this.Pod.Namespace()}");
+ }
+ catch (Exception ex)
+ {
+ this.Logger.LogError("An error occurred while creating the specified pod '{pod}': {ex}", $"{this.Pod.Name()}.{this.Pod.Namespace()}", ex);
+ }
+ await this.ReadPodLogsAsync(cancellationToken).ConfigureAwait(false);
+ }
+
+ ///
+ /// Reads the logs of the container's pod
+ ///
+ /// A
+ /// A new awaitable
+ protected virtual async Task ReadPodLogsAsync(CancellationToken cancellationToken = default)
+ {
+ await this.WaitForReadyAsync(cancellationToken);
+ var logStream = await this.Kubernetes.CoreV1.ReadNamespacedPodLogAsync(this.Pod.Name(), this.Pod.Namespace(), cancellationToken: cancellationToken).ConfigureAwait(false);
+ this.StandardOutput = new StreamReader(logStream);
+ }
+
+ ///
+ /// Waits until the pod becomes available
+ ///
+ /// A
+ /// A new awaitable
+ protected virtual async Task WaitForReadyAsync(CancellationToken cancellationToken = default)
+ {
+ this.Logger.LogDebug("Waiting for pod '{pod}'...", $"{this.Pod.Name()}.{this.Pod.Namespace()}");
+ this.Pod = await this.Kubernetes.CoreV1.ReadNamespacedPodAsync(this.Pod.Name(), this.Pod.Namespace(), cancellationToken: cancellationToken);
+ while (this.Pod.Status.Phase == "Pending")
+ {
+ await Task.Delay(100, cancellationToken).ConfigureAwait(false);
+ this.Pod = await this.Kubernetes.CoreV1.ReadNamespacedPodAsync(this.Pod.Name(), this.Pod.Namespace(), cancellationToken: cancellationToken);
+ }
+ this.Logger.LogDebug("The pod '{pod}' is up and running", $"{this.Pod.Name()}.{this.Pod.Namespace()}");
+ }
+
+ ///
+ public virtual async Task WaitForExitAsync(CancellationToken cancellationToken = default)
+ {
+ var response = this.Kubernetes.CoreV1.ListNamespacedPodWithHttpMessagesAsync(this.Pod.Namespace(), fieldSelector: $"metadata.name={Pod.Name()}", cancellationToken: cancellationToken);
+ await foreach (var (_, item) in response.WatchAsync(cancellationToken: cancellationToken).ConfigureAwait(false))
+ {
+ if (item.Status.Phase != "Succeeded" || item.Status.Phase != "Failed") continue;
+ var containerStatus = item.Status.ContainerStatuses.FirstOrDefault();
+ this.ExitCode = containerStatus?.State.Terminated?.ExitCode ?? -1;
+ break;
+ }
+ }
+
+ ///
+ public virtual async Task StopAsync(CancellationToken cancellationToken = default)
+ {
+ await this.Kubernetes.CoreV1.DeleteNamespacedPodAsync(this.Pod.Name(), this.Pod.Namespace(), cancellationToken: cancellationToken).ConfigureAwait(false);
+ await this.CancellationTokenSource.CancelAsync().ConfigureAwait(false);
+ }
+
+ ///
+ /// Disposes of the
+ ///
+ /// A boolean indicating whether or not the is being disposed of
+ /// A new awaitable
+ protected virtual async ValueTask DisposeAsync(bool disposing)
+ {
+ if (!this._disposed) return;
+ this.StandardOutput?.Dispose();
+ this.StandardError?.Dispose();
+ this._disposed = true;
+ await Task.CompletedTask.ConfigureAwait(false);
+ }
+
+ ///
+ public async ValueTask DisposeAsync()
+ {
+ await this.DisposeAsync(true).ConfigureAwait(false);
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Disposes of the
+ ///
+ /// A boolean indicating whether or not the is being disposed of
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!this._disposed) return;
+ this.StandardOutput?.Dispose();
+ this.StandardError?.Dispose();
+ this._disposed = true;
+ }
+
+ ///
+ public void Dispose()
+ {
+ this.Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+}
\ No newline at end of file
diff --git a/src/core/Synapse.Core.Infrastructure.Containers.Kubernetes/KubernetesContainerPlatform.cs b/src/core/Synapse.Core.Infrastructure.Containers.Kubernetes/KubernetesContainerPlatform.cs
new file mode 100644
index 000000000..6d0d9d880
--- /dev/null
+++ b/src/core/Synapse.Core.Infrastructure.Containers.Kubernetes/KubernetesContainerPlatform.cs
@@ -0,0 +1,139 @@
+// Copyright © 2024-Present The Synapse 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.
+
+using k8s;
+using k8s.Models;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using ServerlessWorkflow.Sdk.Models.Processes;
+using Synapse.Core.Infrastructure.Containers.Configuration;
+using Synapse.Core.Infrastructure.Services;
+
+namespace Synapse.Core.Infrastructure.Containers;
+
+///
+/// Represents the Docker implementation of the interface
+///
+/// The current
+/// The service used to perform logging
+/// The current
+/// The current
+public class KubernetesContainerPlatform(IServiceProvider serviceProvider, ILogger logger, IHostEnvironment hostEnvironment, IOptions options)
+ : IHostedService, IContainerPlatform, IDisposable, IAsyncDisposable
+{
+
+ bool _disposed;
+
+ ///
+ /// Gets the current
+ ///
+ protected IServiceProvider ServiceProvider { get; } = serviceProvider;
+
+ ///
+ /// Gets the service used to perform logging
+ ///
+ protected ILogger Logger { get; } = logger;
+
+ ///
+ /// Gets the current
+ ///
+ protected IHostEnvironment Environment { get; } = hostEnvironment;
+
+ ///
+ /// Gets the service used to interact with the Docker API
+ ///
+ protected IKubernetes? Kubernetes { get; set; }
+
+ ///
+ /// Gets the current
+ ///
+ protected KubernetesContainerPlatformOptions Options { get; } = options.Value;
+
+ ///
+ public virtual async Task StartAsync(CancellationToken cancellationToken)
+ {
+ var kubeconfig = string.IsNullOrWhiteSpace(this.Options.Kubeconfig)
+ ? KubernetesClientConfiguration.InClusterConfig()
+ : await KubernetesClientConfiguration.BuildConfigFromConfigFileAsync(new FileInfo(this.Options.Kubeconfig)).ConfigureAwait(false);
+ this.Kubernetes = new Kubernetes(kubeconfig);
+ }
+
+ ///
+ public virtual Task CreateAsync(ContainerProcessDefinition definition, CancellationToken cancellationToken = default)
+ {
+ ArgumentNullException.ThrowIfNull(definition);
+ if (this.Kubernetes == null) throw new NullReferenceException("The KubernetesContainerPlatform has not been properly initialized");
+ var pod = new V1Pod()
+ {
+ Spec = new()
+ {
+ Containers =
+ [
+ new()
+ {
+ Image = definition.Image,
+ ImagePullPolicy = this.Options.ImagePullPolicy,
+ Command = string.IsNullOrWhiteSpace(definition.Command) ? null : ["/bin/sh", "-c", definition.Command],
+ Env = definition.Environment?.Select(e => new V1EnvVar(e.Key, e.Value)).ToList(),
+ RestartPolicy = "Never"
+ }
+ ]
+ }
+ };
+ return Task.FromResult((IContainer)ActivatorUtilities.CreateInstance(this.ServiceProvider, pod, this.Kubernetes));
+ }
+
+ ///
+ public virtual Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
+
+ ///
+ /// Disposes of the
+ ///
+ /// A boolean indicating whether or not the is being disposed of
+ /// A new
+ protected virtual async ValueTask DisposeAsync(bool disposing)
+ {
+ if (this._disposed) return;
+ if (disposing) this.Kubernetes?.Dispose();
+ this._disposed = true;
+ await Task.CompletedTask.ConfigureAwait(false);
+ }
+
+ ///
+ public async ValueTask DisposeAsync()
+ {
+ await this.DisposeAsync(disposing: true).ConfigureAwait(false);
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Disposes of the
+ ///
+ /// A boolean indicating whether or not the is being disposed of
+ protected virtual void Dispose(bool disposing)
+ {
+ if (this._disposed) return;
+ if (disposing) this.Kubernetes?.Dispose();
+ this._disposed = true;
+ }
+
+ ///
+ public void Dispose()
+ {
+ this.Dispose(disposing: true);
+ GC.SuppressFinalize(this);
+ }
+
+}
diff --git a/src/core/Synapse.Core.Infrastructure.Containers.Kubernetes/Synapse.Core.Infrastructure.Containers.Kubernetes.csproj b/src/core/Synapse.Core.Infrastructure.Containers.Kubernetes/Synapse.Core.Infrastructure.Containers.Kubernetes.csproj
new file mode 100644
index 000000000..943123354
--- /dev/null
+++ b/src/core/Synapse.Core.Infrastructure.Containers.Kubernetes/Synapse.Core.Infrastructure.Containers.Kubernetes.csproj
@@ -0,0 +1,49 @@
+
+
+
+ net8.0
+ enable
+ enable
+ en
+ True
+ 1.0.0
+ alpha1
+ $(VersionPrefix)
+ $(VersionPrefix)
+ The Synapse Authors
+ Cloud Native Computing Foundation
+ Copyright © 2024-Present The Synapse Authors. All Rights Reserved.
+ https://github.com/serverlessworkflow/synapse
+ git
+ https://github.com/serverlessworkflow/synapse
+ synapse core infrastructure containers kubernetes k8s
+ true
+ true
+ en
+ Apache-2.0
+ True
+ transparent_logomark_256.png
+ README.md
+ Contains the Kubernetes container platform for running Synapse in a containerized environment.
+ $(VersionPrefix)-$(VersionSuffix)
+ $(VersionPrefix).0
+ $(VersionPrefix).0
+ embedded
+
+
+
+
+ \
+ True
+
+
+ \
+ True
+
+
+
+
+
+
+
+
diff --git a/src/core/Synapse.Core.Infrastructure/Synapse.Core.Infrastructure.csproj b/src/core/Synapse.Core.Infrastructure/Synapse.Core.Infrastructure.csproj
index 2b1975238..ce8ae0c35 100644
--- a/src/core/Synapse.Core.Infrastructure/Synapse.Core.Infrastructure.csproj
+++ b/src/core/Synapse.Core.Infrastructure/Synapse.Core.Infrastructure.csproj
@@ -25,6 +25,7 @@
transparent_logomark_256.png
README.md
Contains essential infrastructure components for the Synapse applications, including utilities and services that support the implementation and management of core functionalities
+ $(VersionPrefix)-$(VersionSuffix)
$(VersionPrefix).0
$(VersionPrefix).0
embedded
diff --git a/src/core/Synapse.Core/Resources/Correlation.yaml b/src/core/Synapse.Core/Resources/Correlation.yaml
index 74b362b12..97bf27b62 100644
--- a/src/core/Synapse.Core/Resources/Correlation.yaml
+++ b/src/core/Synapse.Core/Resources/Correlation.yaml
@@ -1,4 +1,4 @@
-apiVersion: apiextensions.k8s.io/v1
+apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: correlations.synapse.io
diff --git a/src/core/Synapse.Core/Resources/Correlator.yaml b/src/core/Synapse.Core/Resources/Correlator.yaml
index 13ddf81c6..31699cb77 100644
--- a/src/core/Synapse.Core/Resources/Correlator.yaml
+++ b/src/core/Synapse.Core/Resources/Correlator.yaml
@@ -1,4 +1,4 @@
-apiVersion: apiextensions.k8s.io/v1
+apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: correlators.synapse.io
diff --git a/src/core/Synapse.Core/Resources/KubernetesRuntimeConfiguration.cs b/src/core/Synapse.Core/Resources/KubernetesRuntimeConfiguration.cs
index 94f71fa60..d15c4b27f 100644
--- a/src/core/Synapse.Core/Resources/KubernetesRuntimeConfiguration.cs
+++ b/src/core/Synapse.Core/Resources/KubernetesRuntimeConfiguration.cs
@@ -37,7 +37,7 @@ public record KubernetesRuntimeConfiguration
new("runner")
{
Image = SynapseDefaults.Containers.Images.Runner,
- ImagePullPolicy = ImagePullPolicy.Always,
+ ImagePullPolicy = ImagePullPolicy.IfNotPresent,
Ports =
[
new()
@@ -63,6 +63,10 @@ public KubernetesRuntimeConfiguration()
if (!string.IsNullOrWhiteSpace(env)) this.Secrets.VolumeName = env;
env = Environment.GetEnvironmentVariable(SynapseDefaults.EnvironmentVariables.Runtime.Kubernetes.Secrets.MountPath);
if (!string.IsNullOrWhiteSpace(env)) this.Secrets.MountPath = env;
+ env = Environment.GetEnvironmentVariable(SynapseDefaults.EnvironmentVariables.Runtime.Kubernetes.Namespace);
+ if (!string.IsNullOrWhiteSpace(env)) this.Namespace = env;
+ env = Environment.GetEnvironmentVariable(SynapseDefaults.EnvironmentVariables.Runtime.Kubernetes.ServiceAccount);
+ if (!string.IsNullOrWhiteSpace(env)) this.ServiceAccount = env;
}
///
@@ -83,6 +87,18 @@ public KubernetesRuntimeConfiguration()
[DataMember(Order = 3, Name = "secrets"), JsonPropertyOrder(3), JsonPropertyName("secrets"), YamlMember(Order = 3, Alias = "secrets")]
public virtual KubernetesRuntimeSecretsConfiguration Secrets { get; set; } = new();
+ ///
+ /// Gets/sets the namespace in which to create runner pods. If not set, defaults to the namespace defined in the
+ ///
+ [DataMember(Order = 4, Name = "namespace"), JsonPropertyOrder(4), JsonPropertyName("namespace"), YamlMember(Order = 4, Alias = "namespace")]
+ public virtual string? Namespace { get; set; }
+
+ ///
+ /// Gets/sets the name of the service account that grants the runner the ability to spawn containers when its container platform has been set to `kubernetes`
+ ///
+ [DataMember(Order = 5, Name = "serviceAccount"), JsonPropertyOrder(5), JsonPropertyName("serviceAccount"), YamlMember(Order = 5, Alias = "serviceAccount")]
+ public virtual string? ServiceAccount { get; set; }
+
///
/// Loads the runner container template
///
diff --git a/src/core/Synapse.Core/Resources/NativeRuntimeConfiguration.cs b/src/core/Synapse.Core/Resources/NativeRuntimeConfiguration.cs
index d0f8c6147..874b7bff9 100644
--- a/src/core/Synapse.Core/Resources/NativeRuntimeConfiguration.cs
+++ b/src/core/Synapse.Core/Resources/NativeRuntimeConfiguration.cs
@@ -21,15 +21,45 @@ public record NativeRuntimeConfiguration
{
///
- /// Gets/sets the path to the file to execute to run a workflow instance
+ /// Gets the default path to the directory that contains the runner binaries
+ ///
+ public static readonly string DefaultDirectory = Path.Combine(AppContext.BaseDirectory, "bin", "runner");
+ ///
+ /// Gets the default path to the runner executable file
+ ///
+ public const string DefaultExecutable = "Synapse.Runner";
+
+ ///
+ /// Initializes a new
///
- [DataMember(Order = 1, Name = "executable"), JsonPropertyOrder(1), JsonPropertyName("executable"), YamlMember(Order = 1, Alias = "executable")]
- public virtual string Executable { get; set; } = "Synapse.Runner";
+ public NativeRuntimeConfiguration()
+ {
+ var env = Environment.GetEnvironmentVariable(SynapseDefaults.EnvironmentVariables.Runtime.Native.Directory);
+ if (!string.IsNullOrWhiteSpace(env))
+ {
+ if (!System.IO.Directory.Exists(env)) throw new FileNotFoundException("The runner directory does not exist or cannot be found", env);
+ this.Directory = env;
+ }
+ env = Environment.GetEnvironmentVariable(SynapseDefaults.EnvironmentVariables.Runtime.Native.Executable);
+ if (!string.IsNullOrWhiteSpace(env))
+ {
+ var filePath = Path.Combine(this.Directory, env);
+ if (!File.Exists(filePath)) throw new FileNotFoundException("The runner executable file does not exist or cannot be found", filePath);
+ this.Executable = env;
+ }
+ }
///
- /// Gets/sets the working directory
+ /// Gets/sets the runner's working directory
///
- [DataMember(Order = 2, Name = "directory"), JsonPropertyOrder(2), JsonPropertyName("directory"), YamlMember(Order = 2, Alias = "directory")]
- public virtual string Directory { get; set; } = Path.Combine(AppContext.BaseDirectory, "bin", "runner");
+ [DataMember(Order = 1, Name = "directory"), JsonPropertyOrder(1), JsonPropertyName("directory"), YamlMember(Order = 1, Alias = "directory")]
+ public virtual string Directory { get; set; } = DefaultDirectory;
+
+ ///
+ /// Gets/sets the path to the file to execute to run a workflow instance
+ ///
+ [DataMember(Order = 2, Name = "executable"), JsonPropertyOrder(2), JsonPropertyName("executable"), YamlMember(Order = 2, Alias = "executable")]
+ public virtual string Executable { get; set; } = DefaultExecutable;
+
}
diff --git a/src/core/Synapse.Core/Resources/Operator.yaml b/src/core/Synapse.Core/Resources/Operator.yaml
index e1f823f56..300cec7d7 100644
--- a/src/core/Synapse.Core/Resources/Operator.yaml
+++ b/src/core/Synapse.Core/Resources/Operator.yaml
@@ -1,4 +1,4 @@
-apiVersion: apiextensions.k8s.io/v1
+apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: operators.synapse.io
diff --git a/src/core/Synapse.Core/Resources/ServiceAccount.yaml b/src/core/Synapse.Core/Resources/ServiceAccount.yaml
index 4f4cf38a1..f0a6a6aa9 100644
--- a/src/core/Synapse.Core/Resources/ServiceAccount.yaml
+++ b/src/core/Synapse.Core/Resources/ServiceAccount.yaml
@@ -1,4 +1,4 @@
-apiVersion: apiextensions.k8s.io/v1
+apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: service-accounts.synapse.io
diff --git a/src/core/Synapse.Core/Resources/Workflow.yaml b/src/core/Synapse.Core/Resources/Workflow.yaml
index 760abf728..35e89e8fb 100644
--- a/src/core/Synapse.Core/Resources/Workflow.yaml
+++ b/src/core/Synapse.Core/Resources/Workflow.yaml
@@ -1,4 +1,4 @@
-apiVersion: apiextensions.k8s.io/v1
+apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: workflows.synapse.io
@@ -10,7 +10,7 @@ spec:
singular: workflow
kind: Workflow
shortNames:
- - wf
+ - wf
versions:
- name: v1
served: true
@@ -21,6 +21,6 @@ spec:
properties:
spec:
type: object
- properties: {} #todo
+ properties: {}
required:
- spec
\ No newline at end of file
diff --git a/src/core/Synapse.Core/Resources/WorkflowInstance.yaml b/src/core/Synapse.Core/Resources/WorkflowInstance.yaml
index 147738fe9..5498213b7 100644
--- a/src/core/Synapse.Core/Resources/WorkflowInstance.yaml
+++ b/src/core/Synapse.Core/Resources/WorkflowInstance.yaml
@@ -1,4 +1,4 @@
-apiVersion: apiextensions.k8s.io/v1
+apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: workflow-instances.synapse.io
diff --git a/src/core/Synapse.Core/Synapse.Core.csproj b/src/core/Synapse.Core/Synapse.Core.csproj
index f5d4d2412..ba1b257b6 100644
--- a/src/core/Synapse.Core/Synapse.Core.csproj
+++ b/src/core/Synapse.Core/Synapse.Core.csproj
@@ -24,6 +24,7 @@
transparent_logomark_256.png
README.md
Contains the definitions for all resources used by Synapse
+ $(VersionPrefix)-$(VersionSuffix)
$(VersionPrefix).0
$(VersionPrefix).0
embedded
diff --git a/src/core/Synapse.Core/SynapseDefaults.cs b/src/core/Synapse.Core/SynapseDefaults.cs
index d8582f456..c2880e8df 100644
--- a/src/core/Synapse.Core/SynapseDefaults.cs
+++ b/src/core/Synapse.Core/SynapseDefaults.cs
@@ -14,6 +14,7 @@
using Synapse.Resources;
using Neuroglia.Data.Infrastructure.ResourceOriented;
using System.Diagnostics;
+using System.Reflection;
namespace Synapse;
@@ -575,6 +576,11 @@ public static class Runtime
///
public const string Prefix = EnvironmentVariables.Prefix + "RUNTIME_";
+ ///
+ /// Gets the environment variable used to configure the runtime mode
+ ///
+ public const string Mode = Prefix + "MODE";
+
///
/// Exposes constants about Docker runtime-related environment variables
///
@@ -682,6 +688,14 @@ public static class Kubernetes
/// Gets the environment variable used to specify the YAML file used to configure the Kubernetes runner pod
///
public const string Pod = Prefix + "POD";
+ ///
+ /// Gets the environment variable used to configure the namespace to create runner pods into
+ ///
+ public const string Namespace = Prefix + "NAMESPACE";
+ ///
+ /// Gets the environment variable used to configure the name of the service account that grants the runner the ability to spawn containers when its container platform has been set to `kubernetes`
+ ///
+ public const string ServiceAccount = Prefix + "SERVICE_ACCOUNT";
///
/// Exposes constants about environment variables used to configure the secrets used by a Docker runtime
@@ -707,6 +721,28 @@ public static class Secrets
}
+ ///
+ /// Exposes constants about Native runtime-related environment variables
+ ///
+ public static class Native
+ {
+
+ ///
+ /// Gets the prefix for all native runtime related environment variables
+ ///
+ public const string Prefix = Runtime.Prefix + "NATIVE_";
+
+ ///
+ /// Gets the environment variable used to configure the working directory that contains the runner binaries
+ ///
+ public const string Directory = Prefix + "DIRECTORY";
+ ///
+ /// Gets the environment variable used to configure the path to the runner's executable file
+ ///
+ public const string Executable = Prefix + "EXECUTABLE";
+
+ }
+
}
///
@@ -780,6 +816,8 @@ public static class Containers
public static class Images
{
+ static string? _version;
+
///
/// Gets the name of the Synapse container image registry
///
@@ -787,7 +825,17 @@ public static class Images
///
/// Gets the current version of Synapse container images
///
- public static readonly string Version = typeof(SynapseDefaults).Assembly.GetName().Version?.ToString(3) ?? "latest";
+ public static string Version
+ {
+ get
+ {
+ if (!string.IsNullOrWhiteSpace(_version)) return _version;
+ _version = typeof(SynapseDefaults).Assembly.GetCustomAttribute()?.InformationalVersion ?? "latest";
+ if (_version.EndsWith('-')) _version = _version[..^1];
+ if (_version.Contains('+')) _version = _version[.._version.IndexOf('+')];
+ return _version;
+ }
+ }
///
/// Gets the name of the Synapse API container image
///
@@ -803,7 +851,7 @@ public static class Images
///
/// Gets the name of the Synapse Runner container image
///
- public static readonly string Runner = $"{ImageRegistry}/runner:latest"; //todo: $"{ImageRegistry}/runner:{Version}";
+ public static readonly string Runner = $"{ImageRegistry}/runner:{Version}";
}
diff --git a/src/correlator/Synapse.Correlator/Synapse.Correlator.csproj b/src/correlator/Synapse.Correlator/Synapse.Correlator.csproj
index 538479c79..1bb52e6f1 100644
--- a/src/correlator/Synapse.Correlator/Synapse.Correlator.csproj
+++ b/src/correlator/Synapse.Correlator/Synapse.Correlator.csproj
@@ -21,6 +21,7 @@
en
Apache-2.0
True
+ $(VersionPrefix)-$(VersionSuffix)
$(VersionPrefix).0
$(VersionPrefix).0
embedded
diff --git a/src/operator/Synapse.Operator/Configuration/OperatorOptions.cs b/src/operator/Synapse.Operator/Configuration/OperatorOptions.cs
index 79a6df113..bd437345a 100644
--- a/src/operator/Synapse.Operator/Configuration/OperatorOptions.cs
+++ b/src/operator/Synapse.Operator/Configuration/OperatorOptions.cs
@@ -26,7 +26,27 @@ public OperatorOptions()
{
this.Namespace = Environment.GetEnvironmentVariable(SynapseDefaults.EnvironmentVariables.Operator.Namespace)!;
this.Name = Environment.GetEnvironmentVariable(SynapseDefaults.EnvironmentVariables.Operator.Name)!;
- var env = Environment.GetEnvironmentVariable(SynapseDefaults.EnvironmentVariables.Runner.Api);
+ var env = Environment.GetEnvironmentVariable(SynapseDefaults.EnvironmentVariables.Runtime.Mode);
+ if (!string.IsNullOrWhiteSpace(env))
+ {
+ this.Runner.Runtime = env switch
+ {
+ OperatorRuntimeMode.Docker => new()
+ {
+ Docker = new()
+ },
+ OperatorRuntimeMode.Kubernetes => new()
+ {
+ Kubernetes = new()
+ },
+ OperatorRuntimeMode.Native => new()
+ {
+ Native = new()
+ },
+ _ => throw new NotSupportedException($"The specified operator runtime mode '{env}' is not supported"),
+ };
+ }
+ env = Environment.GetEnvironmentVariable(SynapseDefaults.EnvironmentVariables.Runner.Api);
if (!string.IsNullOrWhiteSpace(env))
{
this.Runner ??= new();
@@ -43,11 +63,7 @@ public OperatorOptions()
this.Runner.PublishLifecycleEvents = publishLifeCycleEvents;
}
env = Environment.GetEnvironmentVariable(SynapseDefaults.EnvironmentVariables.Runner.ContainerPlatform);
- if (!string.IsNullOrWhiteSpace(env))
- {
- this.Runner ??= new();
- this.Runner.ContainerPlatform = env;
- }
+ if (!string.IsNullOrWhiteSpace(env)) this.Runner.ContainerPlatform = env;
}
///
diff --git a/src/operator/Synapse.Operator/Program.cs b/src/operator/Synapse.Operator/Program.cs
index fc48f6f84..53cc32085 100644
--- a/src/operator/Synapse.Operator/Program.cs
+++ b/src/operator/Synapse.Operator/Program.cs
@@ -42,7 +42,6 @@
});
services.AddSingleton();
services.AddSynapse(context.Configuration);
-
services.AddScoped();
services.AddScoped();
services.AddScoped();
diff --git a/src/operator/Synapse.Operator/Synapse.Operator.csproj b/src/operator/Synapse.Operator/Synapse.Operator.csproj
index ea657364f..267b7233f 100644
--- a/src/operator/Synapse.Operator/Synapse.Operator.csproj
+++ b/src/operator/Synapse.Operator/Synapse.Operator.csproj
@@ -21,6 +21,7 @@
en
Apache-2.0
True
+ $(VersionPrefix)-$(VersionSuffix)
$(VersionPrefix).0
$(VersionPrefix).0
embedded
diff --git a/src/runner/Synapse.Runner/Configuration/RunnerContainerOptions.cs b/src/runner/Synapse.Runner/Configuration/RunnerContainerOptions.cs
index d1eeb0c7d..31ad00d12 100644
--- a/src/runner/Synapse.Runner/Configuration/RunnerContainerOptions.cs
+++ b/src/runner/Synapse.Runner/Configuration/RunnerContainerOptions.cs
@@ -36,10 +36,9 @@ public RunnerContainerOptions()
this.Docker = new();
break;
case ContainerPlatform.Kubernetes:
- //todo: implement Kubernetes
+ this.Kubernetes = new();
break;
- default:
- throw new NotSupportedException($"The specified container platform '{env}' is not supported");
+ default: throw new NotSupportedException($"The specified container platform '{env}' is not supported");
}
}
@@ -48,9 +47,14 @@ public RunnerContainerOptions()
///
public virtual DockerContainerPlatformOptions? Docker { get; set; }
+ ///
+ /// Gets/sets the options used to configure the Kubernetes container platform, if any
+ ///
+ public virtual KubernetesContainerPlatformOptions? Kubernetes { get; set; }
+
///
/// Gets the container platform used by the configured runner
///
- public virtual string Platform => this.Docker != null ? ContainerPlatform.Docker : throw new NullReferenceException("The runner's container platform must be configured");
+ public virtual string Platform => this.Docker != null ? ContainerPlatform.Docker : this.Kubernetes != null ? ContainerPlatform.Kubernetes : throw new NullReferenceException("The runner's container platform must be configured");
}
\ No newline at end of file
diff --git a/src/runner/Synapse.Runner/Program.cs b/src/runner/Synapse.Runner/Program.cs
index 3ce85a9f8..441b2c5e9 100644
--- a/src/runner/Synapse.Runner/Program.cs
+++ b/src/runner/Synapse.Runner/Program.cs
@@ -69,7 +69,7 @@
services.AddDockerContainerPlatform();
break;
case ContainerPlatform.Kubernetes:
- //services.AddKubernetesContainerPlatform(); //todo
+ services.AddKubernetesContainerPlatform();
break;
default:
throw new NotSupportedException($"The specified container platform '{options.Containers.Platform}' is not supported");
diff --git a/src/runner/Synapse.Runner/Services/SecretsManager.cs b/src/runner/Synapse.Runner/Services/SecretsManager.cs
index bd714b0cd..4a0c0dc1c 100644
--- a/src/runner/Synapse.Runner/Services/SecretsManager.cs
+++ b/src/runner/Synapse.Runner/Services/SecretsManager.cs
@@ -48,32 +48,39 @@ public class SecretsManager(ILogger logger, ISerializerProvider
///
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
- var path = string.IsNullOrWhiteSpace(this.Options.Secrets.Directory)
+ try
+ {
+ var path = string.IsNullOrWhiteSpace(this.Options.Secrets.Directory)
? RunnerSecretsOptions.DefaultDirectory
: this.Options.Secrets.Directory;
- var directory = new DirectoryInfo(path);
- if (!directory.Exists) directory.Create();
- foreach (var file in directory.GetFiles())
- {
- using var stream = file.OpenRead();
- var mediaTypeName = MimeTypes.GetMimeType(file.Name);
- var serializer = this.SerializerProvider.GetSerializersFor(mediaTypeName).FirstOrDefault();
- if (serializer == null)
- {
- this.Logger.LogWarning("Skipped loading secret '{secretFile}': failed to find a serializer for the specified media type '{mediaType}'", file.Name, mediaTypeName);
- continue;
- }
- try
+ var directory = new DirectoryInfo(path);
+ if (!directory.Exists) directory.Create();
+ foreach (var file in directory.GetFiles())
{
- var secret = serializer.Deserialize
/// The associated with the
+/// The service used to perform logging
/// The service used to interact with the Kubernetes API
-public class KubernetesWorkflowProcess(V1Pod pod, IKubernetes kubernetes)
+public class KubernetesWorkflowProcess(V1Pod pod, ILogger logger, IKubernetes kubernetes)
: WorkflowProcessBase
{
@@ -37,6 +39,11 @@ public class KubernetesWorkflowProcess(V1Pod pod, IKubernetes kubernetes)
///
protected V1Pod Pod { get; set; } = pod;
+ ///
+ /// Gets the service used to perform logging
+ ///
+ protected ILogger Logger { get; } = logger;
+
///
/// Gets the service used to interact with the Kubernetes API
///
@@ -69,7 +76,16 @@ public class KubernetesWorkflowProcess(V1Pod pod, IKubernetes kubernetes)
///
public override async Task StartAsync(CancellationToken cancellationToken = default)
{
- this.Pod = await this.Kubernetes.CoreV1.CreateNamespacedPodAsync(this.Pod, this.Pod.Namespace(), cancellationToken: cancellationToken);
+ try
+ {
+ this.Logger.LogDebug("Creating pod '{pod}'...", $"{this.Pod.Name()}.{this.Pod.Namespace()}");
+ this.Pod = await this.Kubernetes.CoreV1.CreateNamespacedPodAsync(this.Pod, this.Pod.Namespace(), cancellationToken: cancellationToken);
+ this.Logger.LogDebug("The pod '{pod}' has been successfully created", $"{this.Pod.Name()}.{this.Pod.Namespace()}");
+ }
+ catch(Exception ex)
+ {
+ this.Logger.LogError("An error occurred while creating the specified pod '{pod}': {ex}", $"{this.Pod.Name()}.{this.Pod.Namespace()}", ex);
+ }
_ = Task.Run(() => this.ReadPodLogsAsync(cancellationToken), cancellationToken);
}
@@ -94,12 +110,14 @@ protected virtual async Task ReadPodLogsAsync(CancellationToken cancellationToke
/// A new awaitable
protected virtual async Task WaitForReadyAsync(CancellationToken cancellationToken = default)
{
+ this.Logger.LogDebug("Waiting for pod '{pod}'...", $"{this.Pod.Name()}.{this.Pod.Namespace()}");
this.Pod = await this.Kubernetes.CoreV1.ReadNamespacedPodAsync(this.Pod.Name(), this.Pod.Namespace(), cancellationToken: cancellationToken);
while (this.Pod.Status.Phase == "Pending")
{
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
this.Pod = await this.Kubernetes.CoreV1.ReadNamespacedPodAsync(this.Pod.Name(), this.Pod.Namespace(), cancellationToken: cancellationToken);
}
+ this.Logger.LogDebug("The pod '{pod}' is up and running", $"{this.Pod.Name()}.{this.Pod.Namespace()}");
}
///
diff --git a/src/runtime/Synapse.Runtime.Kubernetes/Synapse.Runtime.Kubernetes.csproj b/src/runtime/Synapse.Runtime.Kubernetes/Synapse.Runtime.Kubernetes.csproj
index 031380734..77187a6e5 100644
--- a/src/runtime/Synapse.Runtime.Kubernetes/Synapse.Runtime.Kubernetes.csproj
+++ b/src/runtime/Synapse.Runtime.Kubernetes/Synapse.Runtime.Kubernetes.csproj
@@ -25,6 +25,7 @@
transparent_logomark_256.png
README.md
Contains the services for running workflows in Kubernetes
+ $(VersionPrefix)-$(VersionSuffix)
$(VersionPrefix).0
$(VersionPrefix).0
embedded
diff --git a/src/runtime/Synapse.Runtime.Native/Synapse.Runtime.Native.csproj b/src/runtime/Synapse.Runtime.Native/Synapse.Runtime.Native.csproj
index ae0e909bd..8a7bcedde 100644
--- a/src/runtime/Synapse.Runtime.Native/Synapse.Runtime.Native.csproj
+++ b/src/runtime/Synapse.Runtime.Native/Synapse.Runtime.Native.csproj
@@ -25,6 +25,7 @@
transparent_logomark_256.png
README.md
Contains the services for running Synapse natively
+ $(VersionPrefix)-$(VersionSuffix)
$(VersionPrefix).0
$(VersionPrefix).0
embedded