Skip to content

Commit

Permalink
Merge pull request #10 from DiamondLightSource/from-scratch
Browse files Browse the repository at this point in the history
Allow deployments from scratch
  • Loading branch information
MJGaughran authored Nov 29, 2024
2 parents cebcd0a + 1df65c8 commit 89d3726
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 33 deletions.
14 changes: 12 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"request": "launch",
"justMyCode": false,
"program": "/venv/bin/deploy-tools",
"args": "validate ${input:deploy-folder} ${input:config-folder}",
"args": "validate ${input:from-scratch} ${input:deploy-folder} ${input:config-folder}",
"console": "integratedTerminal",
"env": {
// Enable break on exception when debugging tests (see: tests/conftest.py)
Expand All @@ -64,7 +64,7 @@
"request": "launch",
"justMyCode": false,
"program": "/venv/bin/deploy-tools",
"args": "sync ${input:deploy-folder} ${input:config-folder}",
"args": "sync ${input:from-scratch} ${input:deploy-folder} ${input:config-folder}",
"console": "integratedTerminal",
"env": {
// Enable break on exception when debugging tests (see: tests/conftest.py)
Expand All @@ -91,5 +91,15 @@
"default": "${workspaceFolder}/src/deploy_tools/demo_configuration/",
"type": "promptString"
},
{
"id": "from-scratch",
"description": "Allow a complete redeployment to an empty directory",
"options": [
"--from-scratch",
"--no-from-scratch"
],
"default": "--no-from-scratch",
"type": "pickString"
},
]
}
14 changes: 12 additions & 2 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
{
"label": "Sync modules",
"type": "shell",
"command": "deploy-tools sync ${input:deploy-folder} ${input:config-folder}",
"command": "deploy-tools sync ${input:from-scratch} ${input:deploy-folder} ${input:config-folder}",
"problemMatcher": []
},
{
Expand All @@ -33,7 +33,7 @@
{
"label": "Validate deployment",
"type": "shell",
"command": "deploy-tools validate ${input:deploy-folder} ${input:config-folder}",
"command": "deploy-tools validate ${input:from-scratch} ${input:deploy-folder} ${input:config-folder}",
"problemMatcher": []
},
{
Expand Down Expand Up @@ -62,5 +62,15 @@
"default": "${workspaceFolder}/src/deploy_tools/demo_configuration/",
"type": "promptString"
},
{
"id": "from-scratch",
"description": "Allow a complete redeployment to an empty directory",
"options": [
"--from-scratch",
"--no-from-scratch"
],
"default": "--no-from-scratch",
"type": "pickString"
},
]
}
6 changes: 4 additions & 2 deletions src/deploy_tools/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ def sync(
dir_okay=True,
),
],
from_scratch: Annotated[bool, typer.Option()] = False,
) -> None:
"""Sync deployment folder with current configuration"""
synchronise(deployment_root, config_folder)
synchronise(deployment_root, config_folder, from_scratch)


@app.command(no_args_is_help=True)
Expand All @@ -58,11 +59,12 @@ def validate(
dir_okay=True,
),
],
from_scratch: Annotated[bool, typer.Option()] = False,
) -> None:
"""Validate deployment configuration and print a list of modules for deployment.
This is the same validation that the deploy-tools sync command uses."""
validate_configuration(deployment_root, config_folder)
validate_configuration(deployment_root, config_folder, from_scratch)


@app.command(no_args_is_help=True)
Expand Down
5 changes: 4 additions & 1 deletion src/deploy_tools/deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,12 @@ def deploy_new_releases(to_add: list[Release], layout: Layout) -> None:
for release in to_add:
name = release.module.name
version = release.module.version
deprecated = release.deprecated

built_modulefile = layout.get_built_modulefile(name, version)
modulefile_link = layout.get_modulefile(name, version)
modulefile_link = layout.get_modulefile(
name, version, from_deprecated=deprecated
)

modulefile_link.parent.mkdir(parents=True, exist_ok=True)
os.symlink(built_modulefile, modulefile_link)
Expand Down
22 changes: 14 additions & 8 deletions src/deploy_tools/snapshot.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,19 @@ def create_snapshot(deployment: Deployment, layout: Layout) -> None:
yaml.safe_dump(deployment.model_dump(), f)


def load_snapshot(layout: Layout, allow_empty: bool = True) -> Deployment:
if not layout.deployment_snapshot_path.exists():
if allow_empty:
return Deployment(settings=DeploymentSettings(), releases={})

raise SnapshotError(
f"Cannot load deployment snapshot:\n{layout.deployment_snapshot_path}"
)
def load_snapshot(layout: Layout, from_scratch: bool = False) -> Deployment:
if from_scratch:
if not layout.deployment_root.exists():
raise SnapshotError(
f"Deployment root does not exist:\n" f"{layout.deployment_root}"
)

if layout.deployment_snapshot_path.exists():
raise SnapshotError(
f"Deployment snapshot must not exist when deploying from scratch:\n"
f"{layout.deployment_snapshot_path}"
)

return Deployment(settings=DeploymentSettings(), releases={})

return load_from_yaml(Deployment, layout.deployment_snapshot_path)
8 changes: 5 additions & 3 deletions src/deploy_tools/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@
)


def synchronise(deployment_root: Path, config_folder: Path) -> None:
def synchronise(
deployment_root: Path, config_folder: Path, from_scratch: bool = False
) -> None:
"""Synchronise the deployment folder with the current configuration"""
deployment = load_deployment(config_folder)
layout = Layout(deployment_root)
snapshot = load_snapshot(layout)
snapshot = load_snapshot(layout, from_scratch)

deployment_changes = validate_deployment_changes(deployment, snapshot)
deployment_changes = validate_deployment_changes(deployment, snapshot, from_scratch)

check_deploy_actions(deployment_changes, layout)

Expand Down
37 changes: 22 additions & 15 deletions src/deploy_tools/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,22 @@ class ValidationError(Exception):
pass


def validate_configuration(deployment_root: Path, config_folder: Path) -> None:
def validate_configuration(
deployment_root: Path, config_folder: Path, from_scratch: bool = False
) -> None:
"""Validate deployment configuration and print a list of modules for deployment.
The validate_* functions consider only the current and previous deployment
to identify what changes need to be made, while check_actions will look at the
current deployment area to ensure that the specified actions can be completed."""
with TemporaryDirectory() as build_dir:
deployment = load_deployment(config_folder)
layout = Layout(deployment_root, Path(build_dir))
snapshot = load_snapshot(layout)
layout = Layout(deployment_root, build_root=Path(build_dir))
snapshot = load_snapshot(layout, from_scratch)

deployment_changes = validate_deployment_changes(deployment, snapshot)
deployment_changes = validate_deployment_changes(
deployment, snapshot, from_scratch
)

check_deploy_actions(deployment_changes, layout)

Expand Down Expand Up @@ -89,28 +93,30 @@ def print_version_updates(


def validate_deployment_changes(
deployment: Deployment, snapshot: Deployment
deployment: Deployment, snapshot: Deployment, from_scratch: bool
) -> DeploymentChanges:
release_changes = validate_release_changes(deployment, snapshot)
release_changes = validate_release_changes(deployment, snapshot, from_scratch)
default_versions = validate_default_versions(deployment)
return DeploymentChanges(
release_changes=release_changes, default_versions=default_versions
)


def validate_release_changes(
deployment: Deployment, snapshot: Deployment
deployment: Deployment, snapshot: Deployment, from_scratch: bool
) -> ReleaseChanges:
"""Validate configuration to get set of actions that need to be carried out."""
old_releases = snapshot.releases
new_releases = deployment.releases

validate_module_dependencies(deployment)
return get_release_changes(old_releases, new_releases)
return get_release_changes(old_releases, new_releases, from_scratch)


def get_release_changes(
old_releases: ReleasesByNameAndVersion, new_releases: ReleasesByNameAndVersion
old_releases: ReleasesByNameAndVersion,
new_releases: ReleasesByNameAndVersion,
from_scratch: bool,
) -> ReleaseChanges:
release_changes = ReleaseChanges()
for name in new_releases:
Expand Down Expand Up @@ -148,15 +154,15 @@ def get_release_changes(
if version not in new_releases[name]:
release_changes.to_remove.append(old_release)

validate_added_modules(release_changes.to_add)
validate_added_modules(release_changes.to_add, from_scratch)
validate_updated_modules(release_changes.to_update)
validate_deprecated_modules(release_changes.to_deprecate)
validate_removed_modules(release_changes.to_remove)

return release_changes


def validate_added_modules(releases: list[Release]) -> None:
def validate_added_modules(releases: list[Release], from_scratch: bool) -> None:
for release in releases:
module = release.module
if release.deprecated:
Expand All @@ -166,10 +172,11 @@ def validate_added_modules(releases: list[Release]) -> None:
f"deprecated as it is in development mode."
)

raise ValidationError(
f"Module {module.name}/{module.version} cannot have deprecated "
f"status on initial creation."
)
if not from_scratch:
raise ValidationError(
f"Module {module.name}/{module.version} cannot have deprecated "
f"status on initial creation."
)


def validate_updated_modules(releases: list[Release]) -> None:
Expand Down

0 comments on commit 89d3726

Please sign in to comment.