/// <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);
Esempio n. 7
0
 public AddV1WorkflowInstance(V1WorkflowInstance workflowInstance)
 {
     this.WorkflowInstance = workflowInstance;
 }