Skip to content

Commit

Permalink
Add forceDeploy option
Browse files Browse the repository at this point in the history
  • Loading branch information
henrist committed Mar 28, 2024
1 parent 707fb0e commit a73a772
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 12 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ POST /deploy
"service": "service-name",
"attributes": {
"image": "hello-world"
}
},
"forceDeploy": false
}
```

Expand Down
6 changes: 5 additions & 1 deletion deployer/api.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import os

from flask import Blueprint, Response, current_app, request
from pydantic import BaseModel, StrictStr, ValidationError
from pydantic import BaseModel, StrictBool, StrictStr, ValidationError
from werkzeug.exceptions import BadRequest

from deployer.config import Config
Expand All @@ -12,6 +12,9 @@
class DeployRequest(BaseModel):
service: StrictStr
attributes: dict[StrictStr, StrictStr]
# Normally deploys only when an attribute is changed.
# Set this to true to always do a deploy for the service.
forceDeploy: StrictBool = False


def text_response(value, status):
Expand Down Expand Up @@ -64,6 +67,7 @@ def deploy(service_locks: ServiceLocks, config: Config, deployer: Deployer):
deployer.handle(
service=service,
attributes=model.attributes,
force_deploy=model.forceDeploy,
)

return text_response("OK\n", 200)
Expand Down
24 changes: 14 additions & 10 deletions deployer/deployer.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ def _write_deployer_file(
) -> None:
deployer_json_file.write_text(json.dumps(content, indent=" ") + "\n")

def handle(self, service: ServiceModel, attributes: dict[str, str]):
def handle(
self, service: ServiceModel, attributes: dict[str, str], force_deploy: bool
):
patch_values: dict[str, str] = {}
for key, value in attributes.items():
if key not in service.mappings:
Expand All @@ -106,9 +108,10 @@ def handle(self, service: ServiceModel, attributes: dict[str, str]):
updated_content = self._patch_content(previous_content, patch_values)
if previous_content == updated_content:
logger.info("No changes found")
return

self._write_deployer_file(deployer_json_file, updated_content)
if not force_deploy:
return
else:
self._write_deployer_file(deployer_json_file, updated_content)

start = time.time()

Expand All @@ -120,12 +123,13 @@ def handle(self, service: ServiceModel, attributes: dict[str, str]):

logger.info(f"Ansible deploy completed in {time.time() - start} s")

self._write_changes(
repo=repo,
service=service,
deployer_json_file=deployer_json_file,
patch_values=patch_values,
)
if previous_content != updated_content:
self._write_changes(
repo=repo,
service=service,
deployer_json_file=deployer_json_file,
patch_values=patch_values,
)

finally:
repo.cleanup()
49 changes: 49 additions & 0 deletions tests/test_deployer.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def test_write_retry(
attributes={
"value": "hello",
},
force_deploy=False,
)

assert mock_push_changes.call_count == 2
Expand All @@ -55,6 +56,54 @@ def test_ansible(
attributes={
"value": "hello",
},
force_deploy=False,
)

mock_push_changes.assert_called_once()

@patch("deployer.repo.TempRepo.push_changes")
@patch("deployer.deployer.Deployer._ansible_deploy")
def test_skip_no_change(
self,
mock_ansible_deploy: MagicMock,
mock_push_changes: MagicMock,
injector: Injector,
):
# Avoid any real side effect.
mock_ansible_deploy.return_value = None
mock_push_changes.return_value = None

config = injector.get(Config)
deployer = Deployer(config=config)

deployer.handle(
service=config.services["test-service1"],
attributes={},
force_deploy=False,
)

mock_ansible_deploy.assert_not_called()

@patch("deployer.repo.TempRepo.push_changes")
@patch("deployer.deployer.Deployer._ansible_deploy")
def test_force_deploy_for_no_changes(
self,
mock_ansible_deploy: MagicMock,
mock_push_changes: MagicMock,
injector: Injector,
):
# Avoid any real side effect.
mock_ansible_deploy.return_value = None
mock_push_changes.return_value = None

config = injector.get(Config)
deployer = Deployer(config=config)

deployer.handle(
service=config.services["test-service1"],
attributes={},
force_deploy=True,
)

mock_ansible_deploy.assert_called_once()
mock_push_changes.assert_not_called()

0 comments on commit a73a772

Please sign in to comment.