Skip to content

Commit 684760c

Browse files
committed
Have setup_kubernetes_job prioritize services whose git_shas are changing, so that real bounces take priority over big bounces.
1 parent 08f4ca8 commit 684760c

File tree

2 files changed

+129
-0
lines changed

2 files changed

+129
-0
lines changed

paasta_tools/setup_kubernetes_job.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import argparse
2424
import logging
2525
import sys
26+
from typing import Dict
2627
from typing import List
2728
from typing import Optional
2829
from typing import Sequence
@@ -213,6 +214,12 @@ def setup_kube_deployments(
213214
for deployment in existing_kube_deployments
214215
}
215216

217+
existing_git_shas: Dict[Tuple[str, str, str], List[str]] = {}
218+
for deployment in existing_kube_deployments:
219+
existing_git_shas.setdefault(
220+
(deployment.service, deployment.instance, deployment.namespace), []
221+
).append(deployment.git_sha)
222+
216223
applications = [
217224
create_application_object(
218225
cluster=cluster,
@@ -223,6 +230,31 @@ def setup_kube_deployments(
223230
else (_, None)
224231
for _, service_instance in service_instance_configs_list
225232
]
233+
234+
def sort_key(ok_app: Tuple[bool, Optional[Application]]) -> int:
235+
"""This will return 1 if the desired git_sha matches an existing git_sha, and 0 otherwise. This will cause applications that need a new git_sha
236+
to be handled first.
237+
This prioritizes "real" bounces (developers pushing new service versions) over things that are likely to be big bounces (config-only changes).
238+
Most of the time, this won't matter much, as we should get through our backlog quickly, but when there is a backlog, we want to avoid blocking developers.
239+
"""
240+
ok, app = ok_app
241+
if ok:
242+
if app.kube_deployment.git_sha in existing_git_shas.get(
243+
(
244+
app.kube_deployment.service,
245+
app.kube_deployment.instance,
246+
app.kube_deployment.namespace,
247+
),
248+
[],
249+
):
250+
return 1
251+
else:
252+
return 0
253+
else:
254+
return 2
255+
256+
applications.sort(key=sort_key)
257+
226258
api_updates = 0
227259
for _, app in applications:
228260
if app:

tests/test_setup_kubernetes_job.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,3 +654,100 @@ def test_setup_kube_deployments_skip_malformed_apps():
654654
assert mock_log_obj.exception.call_args_list[0] == mock.call(
655655
"Error while processing: fake_app"
656656
)
657+
658+
659+
def test_setup_kube_deployments_does_git_sha_changes_first():
660+
with mock.patch(
661+
"paasta_tools.setup_kubernetes_job.create_application_object",
662+
autospec=True,
663+
) as mock_create_application_object, mock.patch(
664+
"paasta_tools.setup_kubernetes_job.list_all_paasta_deployments", autospec=True
665+
) as mock_list_all_paasta_deployments, mock.patch(
666+
"paasta_tools.setup_kubernetes_job.log", autospec=True
667+
) as mock_log_obj:
668+
mock_client = mock.Mock()
669+
mock_kube_deploy_config_fm = KubernetesDeploymentConfig(
670+
service="kurupt",
671+
instance="fm",
672+
cluster="fake_cluster",
673+
config_dict={},
674+
branch_dict=None,
675+
)
676+
mock_kube_deploy_config_garage = KubernetesDeploymentConfig(
677+
service="kurupt",
678+
instance="garage",
679+
cluster="fake_cluster",
680+
config_dict={},
681+
branch_dict=None,
682+
)
683+
684+
mock_list_all_paasta_deployments.return_value = [
685+
KubeDeployment(
686+
service="kurupt",
687+
instance="garage",
688+
git_sha="1",
689+
namespace="paasta",
690+
image_version="extrastuff-1",
691+
config_sha="1",
692+
replicas=1,
693+
),
694+
KubeDeployment(
695+
service="kurupt",
696+
instance="fm",
697+
git_sha="1",
698+
namespace="paasta",
699+
image_version="extrastuff-1",
700+
config_sha="1",
701+
replicas=1,
702+
),
703+
]
704+
705+
mock_service_instance_configs_list = [
706+
(True, mock_kube_deploy_config_garage),
707+
(True, mock_kube_deploy_config_fm),
708+
]
709+
710+
garage_app = mock.Mock(
711+
kube_deployment=mock.Mock(
712+
git_sha="1",
713+
service="kurupt",
714+
instance="garage",
715+
namespace="paasta",
716+
),
717+
)
718+
fm_app = mock.Mock(
719+
kube_deployment=mock.Mock(
720+
git_sha="2",
721+
service="kurupt",
722+
instance="fm",
723+
namespace="paasta",
724+
),
725+
)
726+
727+
def create_application_object(
728+
cluster: str,
729+
soa_dir: str,
730+
service_instance_config: KubernetesDeploymentConfig,
731+
):
732+
if service_instance_config.instance == "garage":
733+
return (True, garage_app)
734+
elif service_instance_config.instance == "fm":
735+
return (True, fm_app)
736+
raise ValueError("expecting instance of 'fm' or 'garage'")
737+
738+
mock_create_application_object.side_effect = create_application_object
739+
740+
setup_kube_deployments(
741+
kube_client=mock_client,
742+
service_instance_configs_list=mock_service_instance_configs_list,
743+
cluster="fake_cluster",
744+
soa_dir="/nail/blah",
745+
rate_limit=1,
746+
)
747+
748+
# With rate_limit == 1, only the app with a different `git_sha` should be bounced.
749+
assert garage_app.update.call_count == 0
750+
assert fm_app.update.call_count == 1
751+
mock_log_obj.info.assert_any_call(
752+
"Not doing any further updates as we reached the limit (1)"
753+
)

0 commit comments

Comments
 (0)