Lifecycle entities

Internally RKD has three types of objects that are used across the application - Task creation, Task usage declaration, Task execution scheduling.

1) Task Creation

TaskInterface implementations are considered to provide importable Tasks to be used in any automation project.

Example: I have PostgreSQLRunTask and I import it as :pgsql:start

# ...

class RenderDirectoryTask(TaskInterface):
    """Renders *.j2 files recursively in a directory to other directory"""

    def get_name(self) -> str:
        return ':directory-to-directory'

    def get_group_name(self) -> str:
        return ':j2'

    def execute(self, context: ExecutionContext) -> bool:
        # ...

Tasks should be defined mainly as part of installable libraries via PyPI, but could be also defined in local repository.

2) Task usage declaration - importing & preconfiguring Tasks in project code

TaskDeclaration declares that imported TaskInterface implementation would be used in our automation project under some name, with some environment variables, custom workspace and other little customizations that does not involve changing the code of imported Task.

Pipeline , PipelineTask and PipelineBlock defines complete Pipelines, with error handling, notifications, list of Tasks to execute.

That’s called static declaration of reproducible usage. Tasks are imported into a project defined in code, each Task is preconfigured and ready to be used in reproducible way.

from rkd.core.api.syntax import Pipeline, PipelineTask as Task, PipelineBlock as Block, TaskDeclaration
from rkd.core.standardlib.core import DummyTask
from rkd.core.standardlib.shell import ShellCommandTask

IMPORTS = [
    TaskDeclaration(ShellCommandTask(), internal=True)
]

PIPELINES = [
    Pipeline(
        name=':example',
        to_execute=[
            Block(rescue='...', tasks=[
                Task('...'),
            ]),
            Task('...'),
        ]
    )
]

3) Runtime Task scheduling

Imported Tasks and declared for usage in a project are processed, when executed. This later stage is invisible to end-user and is performed internally on runtime, the entities are not known to the user.

Internally RKD must wrap any TaskDeclaration and process Pipeline into lower-level entities on first stage - resolving & compilation stage.

TaskDeclaration is wrapped by DeclarationScheduledToRun that mixes environment, arguments and other options declared in code with everything that exists during execution that takes place now.

Pipeline is translated into GroupDeclaration and associated ArgumentBlock objects which are no longer strings like :db:start --listen=5432 , but are separate TaskDeclaration objects. There are all @rescue and @error modifiers resolved into objects, so everything is calculated on very early stage and therefore can be validated without disrupting later execution by any simple errors.

Each TaskDeclaration in Pipeline is wrapped into DeclarationBelongingToPipeline which acts very similar to DeclarationScheduledToRun , but it was named differently to distinct between something that was declared in the code ( DeclarationBelongingToPipeline ) from something that contains a set of information how the user invoked the command from shell ( DeclarationScheduledToRun )