Command Line Interface Commands¶
This page documents CLI commands available in orchestrator-core.
The syntax of a CLI command is:
python main.py <command> <sub_command>
Where:
<command>is one of the top-level headings in this page<sub_command>is one of the secondary headings in this page
Some examples:
python main.py db migrate_tasks
python main.py generate workflows
python main.py scheduler show-schedule
Each command can also be run with --help to get information directly in the CLI.
Top level options:
--install-completion [bash|zsh|fish|powershell|pwsh]
Install completion for the specified shell. [default: None]
--show-completion [bash|zsh|fish|powershell|pwsh]
Show completion for the specified shell, to copy it or customize the installation. [default: None]
db¶
Interact with the application database. By default, does nothing, specify main.py db --help for more information.
downgrade
The downgrade command will downgrade the database to the previous revision or to the optionally specified revision.
By default the search index is left untouched. Pass --index to rebuild the search index as part of the
migration. Indexing is opt-in because it can take a long time on large databases; on a production deployment it
is usually better to run python main.py index all as a separate post-deployment step.
Source code in orchestrator-core/orchestrator/core/cli/database.py
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 | |
heads
The heads command command shows the Alembic database heads.
Source code in orchestrator-core/orchestrator/core/cli/database.py
144 145 146 147 148 149 150 151 152 | |
history
The history command lists Alembic revision history/changeset scripts in chronological order.
Source code in orchestrator-core/orchestrator/core/cli/database.py
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 | |
init
Initialize the migrations directory.
This command will initialize a migration directory for the orchestrator core application and setup a correct migration environment. It will also throw an exception when it detects conflicting files and directories.
Source code in orchestrator-core/orchestrator/core/cli/database.py
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | |
merge
Merge database revisions.
It is possible when using multiple git branches in your WFO development lifecycle to have multiple Alembic heads emerge. This command will allow you to merge those two (or more) heads to resolve the issue. You also might need to run this after updating your version of orchestrator-core if there have been schema changes.
Source code in orchestrator-core/orchestrator/core/cli/database.py
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | |
migrate_workflows
The migrate-workflows command creates a migration file based on the difference between workflows in the database and registered WorkflowInstances in your codebase.
BACKUP YOUR DATABASE BEFORE USING THE MIGRATION!
You will be prompted with inputs for new models and resource type updates. Resource type updates are only handled when it’s renamed in all product blocks.
Returns None unless --test is used, in which case it returns:
- tuple:
- list of upgrade SQL statements in string format.
- list of downgrade SQL statements in string format.
CLI Arguments
Arguments:
MESSAGE Migration name [required]
Options:
--test / --no-test Optional boolean if you don't want to generate a migration
file [default: no-test]
Source code in orchestrator-core/orchestrator/core/cli/database.py
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 | |
migrate_tasks
The migrate-tasks command creates a migration file based on the difference between tasks in the database and registered TaskInstances in your codebase.
BACKUP YOUR DATABASE BEFORE USING THE MIGRATION!
You will be prompted with inputs for new models and resource type updates. Resource type updates are only handled when it’s renamed in all product blocks.
Returns None unless --test is used, in which case it returns:
- tuple:
- list of upgrade SQL statements in string format.
- list of downgrade SQL statements in string format.
CLI Arguments
Arguments:
MESSAGE Migration name [required]
Options:
--test / --no-test Optional boolean if you don't want to generate a migration
file [default: no-test]
Source code in orchestrator-core/orchestrator/core/cli/database.py
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 | |
revision
The revision command creates a new Alembic revision file.
Source code in orchestrator-core/orchestrator/core/cli/database.py
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 | |
upgrade
The upgrade command will upgrade the database to the specified revision.
By default the search index is left untouched. Pass --index to rebuild the search index as part of the
migration. Indexing is opt-in because it can take a long time on large databases; on a production deployment it
is usually better to run python main.py index all as a separate post-deployment step.
Source code in orchestrator-core/orchestrator/core/cli/database.py
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | |
migrate-domain-models¶
The migrate-domain-models CLI command is used to
automatically generate the data migrations that you’ll need when you add or
change a Domain Model. It will inspect your DB and the existing domain models,
analyse the differences and it will generate an Alembic data migration in the
correct folder.
Features:
- detect a new Domain Model attribute / resource type
- detect a renamed Domain Model attribute / resource type
- detect a removed Domain Model attribute / resource type
- detect a new Domain Model
- detect a removed Domain Model
- ability to ask for human input when needed
Below in the documentation these features are discussed in more detail.
BACKUP DATABASE BEFORE USING THE MIGRATION!
Arguments
message: Message/description of the generated migration.--test: Optional boolean if you don’t want to generate a migration file.--inputs: stringified dict to prefill inputs. The inputs and updates argument is mostly used for testing, prefilling the given inputs, here examples:- new product:
inputs = { "new_product_name": { "description": "add description", "product_type": "add_type", "tag": "add_tag" }} - new product fixed input:
inputs = { "new_product_name": { "new_fixed_input_name": "value" }} - new product block:
inputs = { "new_product_block_name": { "description": "add description", "tag": "add_tag" } } - new resource type:
inputs = { "new_resource_type_name": { "description": "add description", "value": "add default value", "new_product_block_name": "add default value for block" }}new_product_block_nameprop inserts value specifically for that block.valueprop is inserted as default for all existing instances it is added to.
- new product:
--updates: stringified dict to prefill inputs.- renaming a fixed input:
updates = { "fixed_inputs": { "product_name": { "old_fixed_input_name": "new_fixed_input_name" } } }
- renaming a resource type to a new resource type:
inputs = { "new_resource_type_name": { "description": "add description" }}updates = { "resource_types": { "old_resource_type_name": "new_resource_type_name" } }
- renaming a resource type to existing resource type:
updates = { "resource_types": { "old_resource_type_name": "new_resource_type_name" } }
- renaming a fixed input:
Example¶
You need products in the SUBSCRIPTION_MODEL_REGISTRY, for this example I will use these models (taken out of example-orchestrator):
UserGroup Block¶
from orchestrator.core.domain.base import SubscriptionModel, ProductBlockModel
from orchestrator.core.types import SubscriptionLifecycle
class UserGroupBlockInactive(
ProductBlockModel,
lifecycle=[SubscriptionLifecycle.INITIAL],
product_block_name="UserGroupBlock",
):
group_name: str | None = None
group_id: int | None = None
class UserGroupBlockProvisioning(
UserGroupBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]
):
group_name: str
group_id: int | None = None
class UserGroupBlock(
UserGroupBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]
):
group_name: str
group_id: int
from orchestrator.domain.base import SubscriptionModel, ProductBlockModel
from orchestrator.types import SubscriptionLifecycle
class UserGroupBlockInactive(
ProductBlockModel,
lifecycle=[SubscriptionLifecycle.INITIAL],
product_block_name="UserGroupBlock",
):
group_name: str | None = None
group_id: int | None = None
class UserGroupBlockProvisioning(
UserGroupBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]
):
group_name: str
group_id: int | None = None
class UserGroupBlock(
UserGroupBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]
):
group_name: str
group_id: int
UserGroup Product¶
from orchestrator.core.domain.base import SubscriptionModel
from orchestrator.core.types import SubscriptionLifecycle
from products.product_blocks.user_group import (
UserGroupBlock,
UserGroupBlockInactive,
UserGroupBlockProvisioning,
)
class UserGroupInactive(
SubscriptionModel, is_base=True, lifecycle=[SubscriptionLifecycle.INITIAL]
):
settings: UserGroupBlockInactive
class UserGroupProvisioning(
UserGroupInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]
):
settings: UserGroupBlockProvisioning
class UserGroup(UserGroupProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
settings: UserGroupBlock
from orchestrator.domain.base import SubscriptionModel
from orchestrator.types import SubscriptionLifecycle
from products.product_blocks.user_group import (
UserGroupBlock,
UserGroupBlockInactive,
UserGroupBlockProvisioning,
)
class UserGroupInactive(
SubscriptionModel, is_base=True, lifecycle=[SubscriptionLifecycle.INITIAL]
):
settings: UserGroupBlockInactive
class UserGroupProvisioning(
UserGroupInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]
):
settings: UserGroupBlockProvisioning
class UserGroup(UserGroupProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
settings: UserGroupBlock
User Block¶
from orchestrator.core.domain.base import ProductBlockModel
from orchestrator.core.types import SubscriptionLifecycle
from products.product_blocks.user_group import (
UserGroupBlock,
UserGroupBlockInactive,
UserGroupBlockProvisioning,
)
class UserBlockInactive(
ProductBlockModel,
lifecycle=[SubscriptionLifecycle.INITIAL],
product_block_name="UserBlock",
):
group: UserGroupBlockInactive
username: str | None = None
age: int | None = None
user_id: int | None = None
class UserBlockProvisioning(
UserBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]
):
group: UserGroupBlockProvisioning
username: str
age: int | None = None
user_id: int | None = None
class UserBlock(UserBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
group: UserGroupBlock
username: str
age: int | None = None
user_id: int
from orchestrator.domain.base import ProductBlockModel
from orchestrator.types import SubscriptionLifecycle
from products.product_blocks.user_group import (
UserGroupBlock,
UserGroupBlockInactive,
UserGroupBlockProvisioning,
)
class UserBlockInactive(
ProductBlockModel,
lifecycle=[SubscriptionLifecycle.INITIAL],
product_block_name="UserBlock",
):
group: UserGroupBlockInactive
username: str | None = None
age: int | None = None
user_id: int | None = None
class UserBlockProvisioning(
UserBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]
):
group: UserGroupBlockProvisioning
username: str
age: int | None = None
user_id: int | None = None
class UserBlock(UserBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
group: UserGroupBlock
username: str
age: int | None = None
user_id: int
User Product¶
from orchestrator.core.domain.base import SubscriptionModel
from orchestrator.core.types import SubscriptionLifecycle, strEnum
from products.product_blocks.user import (
UserBlock,
UserBlockInactive,
UserBlockProvisioning,
)
class Affiliation(strEnum):
internal = "internal"
external = "external"
class UserInactive(SubscriptionModel, is_base=True):
affiliation: Affiliation
settings: UserBlockInactive
class UserProvisioning(UserInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]):
affiliation: Affiliation
settings: UserBlockProvisioning
class User(UserProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
affiliation: Affiliation
settings: UserBlock
from orchestrator.domain.base import SubscriptionModel
from orchestrator.types import SubscriptionLifecycle, strEnum
from products.product_blocks.user import (
UserBlock,
UserBlockInactive,
UserBlockProvisioning,
)
class Affiliation(strEnum):
internal = "internal"
external = "external"
class UserInactive(SubscriptionModel, is_base=True):
affiliation: Affiliation
settings: UserBlockInactive
class UserProvisioning(UserInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]):
affiliation: Affiliation
settings: UserBlockProvisioning
class User(UserProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
affiliation: Affiliation
settings: UserBlock
SUBSCRIPTION_MODEL_REGISTRY¶
from orchestrator.core.domain import SUBSCRIPTION_MODEL_REGISTRY
from products.product_types.user import User
from products.product_types.user_group import UserGroup
# Register models to actual definitions for deserialization purposes
SUBSCRIPTION_MODEL_REGISTRY.update(
{
"User group": UserGroup,
"User internal": User,
"User external": User,
}
)
from orchestrator.domain import SUBSCRIPTION_MODEL_REGISTRY
from products.product_types.user import User
from products.product_types.user_group import UserGroup
# Register models to actual definitions for deserialization purposes
SUBSCRIPTION_MODEL_REGISTRY.update(
{
"User group": UserGroup,
"User internal": User,
"User external": User,
}
)
Running the command¶
-
only with a message
python main.py db migrate-domain-models "message" -
Running it as test
python main.py db migrate-domain-models "message" --test -
Running the command with inputs prefilled
python main.py db migrate-domain-models "message" --inputs "{ "" }"
The command will first go through all products and map the differences with the database. debug log example:
2022-10-27 11:45:10 [debug] ProductTable blocks diff [orchestrator.core.domain.base] fixed_inputs_in_db=set() fixed_inputs_model=set() missing_fixed_inputs_in_db=set() missing_fixed_inputs_in_model=set() missing_product_blocks_in_db=set() missing_product_blocks_in_model=set() product_block_db=User group product_blocks_in_db={'UserGroupBlock'} product_blocks_in_model={'UserGroupBlock'}
You will be prompted with inputs when updates are found.
-
rename of resource type input (renaming
agetouser_agein User Block). Only works when the resource type is renamed in all Blocks:Update resource types
Do you wish to rename resource type age to user_age? [y/N]: -
rename of fixed input (renaming
affiliationtoaffiliationingin User Product):Update fixed inputs
Do you wish to rename fixed input affiliation to affiliationing for product User internal? [y/N]: -
update of resource type per block (renaming
agetouser_agein User Block and not choosing to rename resource type). The input will loop until skipped or when there are no options anymore:- first you get to choose which old resource type to update, skip will create/delete all resource types.
> Update block resource types
Which resource type would you want to update in UserBlock Block? 1) age q) skip ? - then you get to choose which new resource type to update with, skip will give you the first question again.
Which resource type should update age? 1) user_age q) skip ? - with 1 and 1, the log level difference would look like:
2023-02-08 14:11:25 [info] update_block_resource_types [orchestrator.core.cli.migrate_domain_models] update_block_resource_types={'UserBlock': {'age': 'user_age'}}
- first you get to choose which old resource type to update, skip will create/delete all resource types.
> Update block resource types
It will log the differences on info level:
2022-10-27 11:45:10 [info] create_products [orchestrator.core.cli.migrate_domain_models] create_products={'User group': <class 'products.product_types.user_group.UserGroup'>, 'User internal': <class 'products.product_types.user.User'>, 'User external': <class 'products.product_types.user.User'>}
2022-10-27 11:45:10 [info] delete_products [orchestrator.core.cli.migrate_domain_models] delete_products=set()
2022-10-27 11:45:10 [info] create_product_fixed_inputs [orchestrator.core.cli.migrate_domain_models] create_product_fixed_inputs={'affiliation': {'User external', 'User internal'}}
2022-10-27 11:45:10 [info] update_product_fixed_inputs [orchestrator.core.cli.migrate_domain_models] update_product_fixed_inputs={}
2022-10-27 11:45:10 [info] delete_product_fixed_inputs [orchestrator.core.cli.migrate_domain_models] delete_product_fixed_inputs={}
2022-10-27 11:45:10 [info] create_product_to_block_relations [orchestrator.core.cli.migrate_domain_models] create_product_to_block_relations={'UserGroupBlock': {'User group'}, 'UserBlock': {'User external', 'User internal'}}
2022-10-27 11:45:10 [info] delete_product_to_block_relations [orchestrator.core.cli.migrate_domain_models] delete_product_to_block_relations={}
2022-10-27 11:45:10 [info] create_resource_types [orchestrator.core.cli.migrate_domain_models] create_resource_types={'username', 'age', 'group_name', 'user_id', 'group_id'}
2022-10-27 11:45:10 [info] rename_resource_types [orchestrator.core.cli.migrate_domain_models] rename_resource_types={}
2022-10-27 11:45:10 [info] delete_resource_types [orchestrator.core.cli.migrate_domain_models] delete_resource_types=set()
2022-10-27 11:45:10 [info] create_resource_type_relations [orchestrator.core.cli.migrate_domain_models] create_resource_type_relations={'group_name': {'UserGroupBlock'}, 'group_id': {'UserGroupBlock'}, 'username': {'UserBlock'}, 'age': {'UserBlock'}, 'user_id': {'UserBlock'}}
2022-10-27 11:45:10 [info] delete_resource_type_relations [orchestrator.core.cli.migrate_domain_models] delete_resource_type_relations={}
2022-10-27 11:45:10 [info] create_product_blocks [orchestrator.core.cli.migrate_domain_models] create_blocks={'UserGroupBlock': <class 'products.product_blocks.user_group.UserGroupBlock'>, 'UserBlock': <class 'products.product_blocks.user.UserBlock'>}
2022-10-27 11:45:10 [info] delete_product_blocks [orchestrator.core.cli.migrate_domain_models] delete_blocks=set()
2022-10-27 11:45:10 [info] create_product_block_relations [orchestrator.core.cli.migrate_domain_models] create_product_block_relations={'UserGroupBlock': {'UserBlock'}}
2022-10-27 11:45:10 [info] delete_product_block_relations [orchestrator.core.cli.migrate_domain_models] delete_product_block_relations={}
You will be asked to confirm the actions in order to continue:
WARNING: Deleting products will also delete its subscriptions.
Confirm the above actions [y/N]:
After confirming, it will start generating the SQL, logging the SQL on debug level and prompt the user for new resources:
-
new product example:
Create new products
Product: UserGroup User group
Supply the production description: User group product
Supply the product tag: GROUP
2022-10-27 11:45:10 [debug] generated SQL [orchestrator.core.cli.domain_gen_helpers.helpers] sql_string=INSERT INTO products (name, description, product_type, tag, status) VALUES ('User group', 'User group product', 'UserGroup', 'GROUP', 'active') RETURNING products.product_id -
new fixed input (the type isn’t checked, so typing an incorrect value will insert in db):
Create fixed inputs
Supply fixed input value for product User internal and fixed input affiliation: internal
Supply fixed input value for product User external and fixed input affiliation: external
2022-10-27 11:45:10 [debug] generated SQL [orchestrator.core.cli.domain_gen_helpers.helpers] sql_string=INSERT INTO fixed_inputs (name, value, product_id) VALUES ('affiliation', 'internal', (SELECT products.product_id FROM products WHERE products.name IN ('User internal'))), ('affiliation', 'external', (SELECT products.product_id FROM products WHERE products.name IN ('User external'))) -
new product block:
Create product blocks
Product block: UserGroupBlock
Supply the product block description: User group settings
Supply the product block tag: UGS
2022-10-27 11:45:10 [debug] generated SQL [orchestrator.core.cli.domain_gen_helpers.helpers] sql_string=`#!sql INSERT INTO product_blocks (name, description, tag, status) VALUES ('UserGroupBlock', 'User group settings', 'UGS', 'active') RETURNING product_blocks.product_block_id` -
new resource type:
Create resource types
Supply description for new resource type group_name: Unique name of user group2022-10-27 11:45:10 [debug] generated SQL [orchestrator.core.cli.domain_gen_helpers.helpers] sql_string=INSERT INTO resource_types (resource_type, description) VALUES ('group_name', 'Unique name of user group') RETURNING resource_types.resource_type_id -
default value for resource type per product block (necessary for adding a default value to existing instances):
Create subscription instance values
Supply default subscription instance value for resource type group_name and product block UserGroupBlock: group2022-10-27 11:45:10 [debug] generated SQL [orchestrator.core.cli.domain_gen_helpers.resource_type_helpers] sql_string= WITH subscription_instance_ids AS ( SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.product_block_id IN ( SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name = 'UserGroupBlock' ) ) INSERT INTO subscription_instance_values (subscription_instance_id, resource_type_id, value) SELECT subscription_instance_ids.subscription_instance_id, resource_types.resource_type_id, 'group' FROM resource_types CROSS JOIN subscription_instance_ids WHERE resource_types.resource_type = 'group_name'
Last part generates the migration with the generated SQL:
Generating migration file
2022-10-27 11:45:10 [info] Version Locations [orchestrator.core.cli.database] locations=/home/tjeerddie/projects_surf/example-orchestrator/migrations/versions/schema /home/tjeerddie/projects_surf/example-orchestrator/.venv/lib/python3.10/site-packages/orchestrator/migrations/versions/schema
Generating /home/tjeerddie/projects_surf/example-orchestrator/migrations/versions/schema/2022-10-27_a8946b2d1647_test.py ... done
Migration generated. Don't forget to create a database backup before migrating!
If you are running with --test, the SQL file will not be generated.
generate¶
Generate products, workflows and other artifacts.
Products can be described in a YAML configuration file which makes it easy to
generate product and product block domain models, and skeleton workflows and
unit tests. Note that this is a one time thing, the generate commands do not
support updating existing products, product-blocks, workflows and migrations,
in this case have a look at the db migrate-domain-models and db migrate-workflows commands.
But it does however help in defining new products with stakeholders, will
generate code that conforms to current workfloworchestrator coding BCP, and
will actually run (although limited in functionality of course).
After describing a new product in a configuration file, the following commands are typically run:
python main.py generate product-blocks
python main.py generate products
python main.py generate workflows
python main.py generate migration
The generate command should be called from the top level folder of your orchestrator
implementation, this is the folder that contains the products sub folder, among others, except when
the --prefix is used to point to that folder. In case there are product blocks defined that use other
generated product blocks, the order in which generate product-blocks is run is important,
the code for the blocks used in other blocks should be generated first.
config file¶
See the Generate Config File guide for full documentation on the YAML product
configuration format used by the generate commands.
migration¶
The python main.py generate migration command creates a migration from a
configuration file.
Options
–config-file - The configuration file [default: None] –python-version - Python version for generated code [default: 3.11] –skip-existing-blocks - If set, the migration will not contain product blocks for which a python implementation exists [default: False]
product¶
The python main.py generate product command creates a product domain model
from a configuration file.
Options
–config-file - The configuration file [default: None]
–dryrun | –no-dryrun - Dry run [default: dryrun]
–force - Force overwrite of existing files
–python-version - Python version for generated code [default: 3.11]
–folder-prefix - Folder prefix, e.g.
product-blocks¶
The python main.py generate product-blocks command creates product block
domain models from a configuration file.
Options
–config-file - The configuration file [default: None]
–dryrun | –no-dryrun - Dry run [default: dryrun]
–force - Force overwrite of existing files
–python-version - Python version for generated code [default: 3.11]
–folder-prefix - Folder prefix, e.g.
unit-tests¶
The python main.py generate unit-tests command creates unit tests from a
configuration file.
Options
–config-file - The configuration file [default: None] –dryrun | –no-dryrun - Dry run [default: dryrun] –force - Force overwrite of existing files –python-version - Python version for generated code [default: 3.11] –tdd - Force test driven development with failing asserts [default: True]
workflows¶
The python main.py generate workflows command creates create, modify,
terminate and validate workflows from a configuration file. The
--custom-templates option can be used to specify a folder with custom
templates to add additional import statements, input form fields and workflow
steps to the create, modify and terminate workflows.
Options
–config-file - The configuration file [default: None]
–dryrun | –no-dryrun - Dry run [default: dryrun]
–force - Force overwrite of existing files
–python-version - Python version for generated code [default: 3.11]
–folder-prefix - Folder prefix, e.g.
Note
The workflows/__init__.py will only be extended with the needed LazyWorkflowInstance
declarations when --force is used.
index¶
(Re-)index the search tables used by AI / Hybrid Search. Run the commands below for the initial build of the index and after bulk changes to subscriptions, products, processes, workflows, product blocks or resource types.
Indexing can take a long time
Indexing a large database can take a long time (up to hours on production deployments). The
db upgrade and db downgrade commands therefore do not update the search index by default. Run the
indexing commands below as a separate post-deployment step, or pass --index to the migration command to opt in.
The syntax of an index command is:
python main.py index <sub_command>
Some examples:
python main.py index all
python main.py index subscriptions --subscription-id <uuid>
python main.py index rebuild-paths
subscriptions¶
Index the subscription search index.
Source code in orchestrator-core/orchestrator/core/cli/search/index_llm.py
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | |
products¶
Index the product search index.
Source code in orchestrator-core/orchestrator/core/cli/search/index_llm.py
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | |
processes¶
Index the process search index.
Source code in orchestrator-core/orchestrator/core/cli/search/index_llm.py
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | |
workflows¶
Index the workflow search index.
Source code in orchestrator-core/orchestrator/core/cli/search/index_llm.py
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | |
product-blocks¶
Index the product block search index.
Source code in orchestrator-core/orchestrator/core/cli/search/index_llm.py
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | |
resource-types¶
Index the resource type search index.
Source code in orchestrator-core/orchestrator/core/cli/search/index_llm.py
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 | |
all¶
Index all entity types and rebuild the ai_search_paths table.
Source code in orchestrator-core/orchestrator/core/cli/search/index_llm.py
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 | |
rebuild-paths¶
Recompute the ai_search_paths distinct-paths table from ai_search_index.
Source code in orchestrator-core/orchestrator/core/cli/search/index_llm.py
257 258 259 260 261 262 263 264 265 266 267 | |
scheduler¶
Commands to interact with the scheduler and scheduled jobs.
run
Starts the scheduler in the foreground.
While running, this process will:
- Periodically wake up when the next schedule is due for execution, and run it
- Process schedule changes made through the schedule API
Source code in orchestrator-core/orchestrator/core/cli/scheduler.py
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | |
force
Force the execution of (a) scheduler(s) based on a schedule ID.
Use the show-schedule command to determine the ID of the schedule to execute.
CLI Arguments
Arguments:
SCHEDULE_ID ID of the schedule to execute
Source code in orchestrator-core/orchestrator/core/cli/scheduler.py
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | |
show_schedule
The show-schedule command shows an overview of the scheduled jobs.
Source code in orchestrator-core/orchestrator/core/cli/scheduler.py
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | |
load_initial_schedule
The load-initial-schedule command loads the initial schedule using the scheduler API.
The initial schedules are
- Task Resume Workflows
- Task Clean Up Tasks
- Task Validate Subscriptions
- Task Validate Products
- Task Validate Awaiting Callbacks
By default, this command is idempotent since v4.7.1 when the scheduler is running. The schedules are only created when they do not already exist in the database. This behavior can be altered through the –recreate option.
Source code in orchestrator-core/orchestrator/core/cli/scheduler.py
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | |
To register your own project’s schedules from code, see Registering your own schedules from code.
load_schedules
Register schedules declared in code, resolving each workflow by name.
This is the entry point core’s own scheduler load-initial-schedule uses, and the supported
way for a project to register its schedules at deploy time, where the REST API is not reachable.
See :func:add_unique_scheduled_task_to_queue for the idempotency caveat.
Parameters:
-
schedules(collections.abc.Sequence[dict[str, typing.Any]]) –Dicts of :class:
APSchedulerJobCreatefields minusworkflow_id, which is resolved fromworkflow_name. -
recreate(bool, default:False) –Whether to delete existing schedule(s) for each workflow first.
Returns:
-
list[str]–The
workflow_nameof each schedule that was skipped because no such workflow is -
list[str]–registered. Raise on a non-empty list to fail a deploy on an unmigrated workflow.
Raises:
-
ValueError–A schedule’s
workflow_nameis missing or not a string, or two schedules name the same workflow — malformed declarations rather than unknown workflows. Uncaught, this exits the CLI non-zero with a traceback. -
ValidationError–A schedule’s remaining fields do not build an :class:
APSchedulerJobCreate, for exampletrigger_kwargsthe trigger rejects.
Every schedule is validated before any of them is queued, so malformed input registers nothing.
Source code in orchestrator-core/orchestrator/core/schedules/service.py
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | |