/// <inheritdoc/> public override Task <IWorkflowProcess> CreateProcessAsync(V1WorkflowInstance workflowInstance, CancellationToken cancellationToken = default) { if (workflowInstance == null) { throw new ArgumentNullException(nameof(workflowInstance)); } var pod = this.BuildWorkerPodFor(workflowInstance); return(Task.FromResult <IWorkflowProcess>(new KubernetesProcess(pod, this.Kubernetes))); }
/// <inheritdoc/> public override async Task <IWorkflowProcess> CreateProcessAsync(V1WorkflowInstance workflowInstance, CancellationToken cancellationToken = default) { if (workflowInstance == null) { throw new ArgumentNullException(nameof(workflowInstance)); } await this.PullWorkerImageAsync(cancellationToken); var containerConfig = this.Options.Runtime.Container; containerConfig.AddOrUpdateEnvironmentVariable(EnvironmentVariables.Api.HostName.Name, EnvironmentVariables.Api.HostName.Value !); //todo: instead, fetch values from options containerConfig.AddOrUpdateEnvironmentVariable(EnvironmentVariables.Runtime.WorkflowInstanceId.Name, workflowInstance.Id.ToString()); //todo: instead, fetch values from options if (this.ApplicationOptions.SkipCertificateValidation) { containerConfig.AddOrUpdateEnvironmentVariable(EnvironmentVariables.SkipCertificateValidation.Name, "true"); } var name = workflowInstance.Id.Replace(":", "-"); var hostConfig = new HostConfig() { Mounts = new List <Mount>() }; if (!string.IsNullOrWhiteSpace(this.Options.Secrets.Directory)) { hostConfig.Mounts.Add(new() { Type = "bind", Source = this.Options.Secrets.Directory, Target = "/run/secrets" }); } var createContainerParameters = new CreateContainerParameters(containerConfig) { Name = name, HostConfig = hostConfig }; var createContainerResult = await this.Docker.Containers.CreateContainerAsync(createContainerParameters, cancellationToken); if (this.Environment.RunsInDocker()) { await this.Docker.Networks.ConnectNetworkAsync(this.Options.Network, new NetworkConnectParameters() { Container = createContainerResult.ID }, cancellationToken); } foreach (var warning in createContainerResult.Warnings) { this.Logger.LogWarning(warning); } return(new DockerProcess(createContainerResult.ID, this.Docker)); }
/// <summary> /// Builds a new worker <see cref="V1Pod"/> for the specified <see cref="V1WorkflowInstance"/> /// </summary> /// <param name="workflowInstance">The <see cref="V1WorkflowInstance"/> to build a new worker <see cref="V1Pod"/> for</param> /// <returns>A new worker <see cref="V1Pod"/></returns> protected virtual V1Pod BuildWorkerPodFor(V1WorkflowInstance workflowInstance) { if (workflowInstance == null) { throw new ArgumentNullException(nameof(workflowInstance)); } var pod = this.Options.WorkerPod; if (pod == null) { throw new Exception($"The '{nameof(KubernetesRuntimeOptions)}.{nameof(KubernetesRuntimeOptions.WorkerPod)}' property must be set and cannot be null"); } if (pod.Metadata == null) { pod.Metadata = new(); } pod.Metadata.Name = workflowInstance.Id; pod.Metadata.NamespaceProperty = EnvironmentVariables.Kubernetes.Namespace.Value; pod.Spec.RestartPolicy = "Never"; if (pod.Spec == null || pod.Spec.Containers == null || !pod.Spec.Containers.Any()) { throw new InvalidOperationException("The specified V1Pod is not valid"); } foreach (var container in pod.Spec.Containers) { if (container.Env == null) { container.Env = new List <V1EnvVar>(); } container.AddOrUpdateEnvironmentVariable(new(EnvironmentVariables.Api.HostName.Name, EnvironmentVariables.Api.HostName.Value !)); container.AddOrUpdateEnvironmentVariable(new(EnvironmentVariables.Runtime.WorkflowInstanceId.Name, workflowInstance.Id)); container.AddOrUpdateEnvironmentVariable(new(EnvironmentVariables.Kubernetes.Namespace.Name, valueFrom: new V1EnvVarSource() { FieldRef = new V1ObjectFieldSelector("metadata.namespace") })); container.AddOrUpdateEnvironmentVariable(new(EnvironmentVariables.Kubernetes.PodName.Name, valueFrom: new V1EnvVarSource() { FieldRef = new V1ObjectFieldSelector("metadata.name") })); } return(pod); }
/// <summary> /// Initializes a new <see cref="WorkflowFacade"/> /// </summary> /// <param name="definition">The current workflow's <see cref="WorkflowDefinition"/></param> /// <param name="instance">The current <see cref="V1WorkflowInstance"/></param> /// <param name="synapseRuntimeApi">The service used to interact with the Synapse Runtime API</param> public WorkflowFacade(WorkflowDefinition definition, V1WorkflowInstance instance, ISynapseRuntimeApi synapseRuntimeApi) { this.Definition = definition; this.Instance = instance; this.SynapseRuntimeApi = synapseRuntimeApi; }
/// <inheritdoc/> public virtual async Task <IOperationResult <Integration.Models.V1WorkflowInstance> > HandleAsync(V1CreateWorkflowInstanceCommand command, CancellationToken cancellationToken = default) { var workflow = await this.Workflows.FindAsync(command.WorkflowId, cancellationToken); if (workflow == null) { throw DomainException.NullReference(typeof(V1Workflow), command.WorkflowId); } var parent = null as V1WorkflowInstance; if (!string.IsNullOrWhiteSpace(command.ParentId)) { parent = await this.WorkflowInstances.FindAsync(command.ParentId, cancellationToken); if (parent == null) { throw DomainException.NullReference(typeof(V1WorkflowInstance), command.ParentId); } } string?key = null; var dataInputSchema = workflow.Definition.DataInputSchema?.Schema; if (dataInputSchema == null && workflow.Definition.DataInputSchemaUri != null) { using var httpClient = this.HttpClientFactory.CreateClient(); var json = await httpClient.GetStringAsync(workflow.Definition.DataInputSchemaUri, cancellationToken); dataInputSchema = JSchema.Parse(json); } if (dataInputSchema != null) { var input = command.InputData; JObject?jobj; if (input == null) { jobj = new JObject(); } else { jobj = JObject.FromObject(input); } if (!jobj.IsValid(dataInputSchema, out IList <string> errors)) { throw new DomainArgumentException($"Invalid workflow input data:{Environment.NewLine}{string.Join(Environment.NewLine, errors)}", nameof(command.InputData)); } } if (!string.IsNullOrWhiteSpace(workflow.Definition.Key) && command.InputData != null) { try { key = this.ExpressionEvaluatorProvider.GetEvaluator(workflow.Definition.ExpressionLanguage) !.Evaluate(workflow.Definition.Key, command.InputData)?.ToString(); } catch { } } if (string.IsNullOrWhiteSpace(key)) { key = Guid.NewGuid().ToBase64(); } while (await this.WorkflowInstances.ContainsAsync(V1WorkflowInstance.BuildUniqueIdentifier(key, workflow), cancellationToken)) { key = Guid.NewGuid().ToBase64(); } var workflowInstance = await this.WorkflowInstances.AddAsync(new(key.ToLowerInvariant(), workflow, command.ActivationType, command.InputData, command.CorrelationContext, parent), cancellationToken); await this.WorkflowInstances.SaveChangesAsync(cancellationToken); workflow.Instanciate(); await this.Workflows.UpdateAsync(workflow, cancellationToken); await this.Workflows.SaveChangesAsync(cancellationToken); if (command.AutoStart) { await this.Mediator.ExecuteAndUnwrapAsync(new V1StartWorkflowInstanceCommand(workflowInstance.Id), cancellationToken); } return(this.Ok(this.Mapper.Map <Integration.Models.V1WorkflowInstance>(workflowInstance))); }
/// <inheritdoc/> public abstract Task <IWorkflowProcess> CreateProcessAsync(V1WorkflowInstance workflowInstance, CancellationToken cancellationToken = default);
public AddV1WorkflowInstance(V1WorkflowInstance workflowInstance) { this.WorkflowInstance = workflowInstance; }