def parse_template( template: WorkflowTemplate, arguments: Dict) -> Tuple[List[ContainerStep], Dict, List[str]]: """Parse a serial workflow template to extract workflow steps and output files. Expands template parameter references in the workflow argument specification and returns the modified argument list as part of the result. Parameters ---------- template: flowserv.model.template.base.WorkflowTemplate Template for a serial workflow. Returns ------- tuple of list of flowsert.controller.serial.workflow.step.ContainerStep, dict and list of string """ # Get the commands from the workflow specification. workflow_spec = template.workflow_spec steps = list() for step in workflow_spec.get('workflow', {}).get('specification', {}).get('steps', []): # Workflow steps may either be parameter references or dictionaries # with `image` and `commands` elements. script = None if tp.is_parameter(step): para = template.parameters[tp.get_name(step)] if para.name in arguments: script = para.cast(arguments[para.name]) else: script = ContainerStep(image=step.get('environment')) for cmd in step.get('commands', []): script.add(cmd) if script: steps.append(script) # Get the workflow arguments that are defined in the workflow template. # Expand template parameter references using the given argument set. run_args = workflow_spec.get('inputs', {}).get('parameters', {}) for key in run_args.keys(): run_args[key] = tp.expand_value(value=str(run_args[key]), arguments=arguments, parameters=template.parameters) # Get the list of output files from the workflow specification. At this # point we do not support references to template arguments or parameters. output_files = workflow_spec.get('outputs', {}).get('files', {}) # Return tuple of workflow steps and output file list. return steps, run_args, output_files
def exec(self, step: ContainerStep, context: Dict, store: FileSystemStorage) -> ExecResult: """Execute a given list of commands that are represented by template strings. Substitutes parameter and template placeholder occurrences first. Then calls the implementation-specific run method to execute the individual commands. Note that the container worker expects a file system storage volume. Parameters ---------- step: flowserv.controller.serial.workflow.ContainerStep Step in a serial workflow. context: dict Dictionary of argument values for parameters in the template. store: flowserv.volume.fs.FileSystemStorage Storage volume that contains the workflow run files. Returns ------- flowserv.controller.serial.workflow.result.ExecResult """ # Create a modified container step where all commands are expended so # that they do not contain references to variables and template parameters # any more. expanded_step = ContainerStep(identifier=step.identifier, image=step.image, env=step.env) for cmd in step.commands: # Generate mapping for template substitution. Include a mapping of # placeholder names to themselves. args = {p: p for p in tp.placeholders(cmd)} args.update(context) # Update arguments with fixed variables. args.update(self.variables) expanded_step.add(Template(cmd).substitute(args).strip()) # Create mapping for environment variables. environment = dict(self.env) environment.update(step.env) environment = environment if environment else None return self.run(step=expanded_step, env=environment, rundir=store.basedir)