qontract-reconcile 0.10.1rc457__py3-none-any.whl → 0.10.1rc458__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: qontract-reconcile
3
- Version: 0.10.1rc457
3
+ Version: 0.10.1rc458
4
4
  Summary: Collection of tools to reconcile services with their desired state as defined in the app-interface DB.
5
5
  Home-page: https://github.com/app-sre/qontract-reconcile
6
6
  Author: Red Hat App-SRE Team
@@ -59,7 +59,7 @@ reconcile/ocm_groups.py,sha256=_kiMUndKc6as6cbbvXxVnq8V_Lj7X5lxMJVCuRWuUFE,2888
59
59
  reconcile/ocm_machine_pools.py,sha256=eebJ6iiTdUcuKE5zBcfNxW1OGmPOvgBtmVu3xNVOoyY,16608
60
60
  reconcile/ocm_update_recommended_version.py,sha256=IYkfLXIprOW1jguZeELcGP1iBPuj-b53R-FTqKulMl8,4204
61
61
  reconcile/ocm_upgrade_scheduler_org_updater.py,sha256=ta8hMJ-su5mRcPpYrvB1COsojXV-SU3PzLPbQhy2Q0I,4190
62
- reconcile/openshift_base.py,sha256=wlxa3wvuySMLCBKThZeY-wwwRSqXGIEMbmETlQ1BXp8,47254
62
+ reconcile/openshift_base.py,sha256=ZESvh2i6Y-0UbctNYgQyChnAt7MJXVePCIAJXhMGruM,49696
63
63
  reconcile/openshift_clusterrolebindings.py,sha256=9lBO2INBDCCtDpoj29NuOPbbpdJUXf-_LQIrhiLmmns,5827
64
64
  reconcile/openshift_groups.py,sha256=tcc9jJtSY_z76BKrN9GzPeClSV2wj_Ymcsmy5LkzsAE,9419
65
65
  reconcile/openshift_limitranges.py,sha256=UvCGo_OQ4XoDK55TJmn55qEhhlkhLzhU12tX8nT5kPQ,3442
@@ -395,7 +395,7 @@ reconcile/test/test_ocm_clusters_manifest_updates.py,sha256=jFRVfc5jby1kI2x_gT6w
395
395
  reconcile/test/test_ocm_machine_pools.py,sha256=3qo6t2Jfr1Wee0NUacyLTDmatp0o7CUNpkVOpHiOiGk,29737
396
396
  reconcile/test/test_ocm_update_recommended_version.py,sha256=iA4BVirTGVXlwcOyeR52IuNO81X_8NR6ZNd7ZFE7igs,4328
397
397
  reconcile/test/test_ocm_upgrade_scheduler_org_updater.py,sha256=zYRGUX7pAmxSv9oFYw2ZnPGa-YAPgDfmqXOJM4eE-8A,4353
398
- reconcile/test/test_openshift_base.py,sha256=owJsyyatl_7z6igUEDeJcMMV5_jIwcV2Yj7YgsmYkXs,27035
398
+ reconcile/test/test_openshift_base.py,sha256=uVsnMghAQhHaJTreeOw4x2INTKJ6qeiZiiteWeKflW8,33874
399
399
  reconcile/test/test_openshift_namespace_labels.py,sha256=P1hqi6P88NijNrurdXG_QR2usyo3EYZSy9zpwYHvDsM,12104
400
400
  reconcile/test/test_openshift_namespaces.py,sha256=HmRnCE5EnFt3MYceVEFHmk8wWRtCrxu2AFGFkY9pdyA,9214
401
401
  reconcile/test/test_openshift_resource.py,sha256=lbTf48jX1q6rGnRiA5pPvfU0uPfY8zhNylMtryn0sLI,12995
@@ -646,8 +646,8 @@ tools/test/test_app_interface_metrics_exporter.py,sha256=SX7qL3D1SIRKFo95FoQztvf
646
646
  tools/test/test_qontract_cli.py,sha256=awwTHEc2DWlykuqGIYM0WOBoSL0KRnOraCLk3C7izis,1401
647
647
  tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
648
648
  tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
649
- qontract_reconcile-0.10.1rc457.dist-info/METADATA,sha256=BRv3LFop8ddTnkGVxnrWDU0ltyxwt4bCkDmbXyrrMwI,2348
650
- qontract_reconcile-0.10.1rc457.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
651
- qontract_reconcile-0.10.1rc457.dist-info/entry_points.txt,sha256=rTjAv28I_CHLM8ID3OPqMI_suoQ9s7tFbim4aYjn9kk,376
652
- qontract_reconcile-0.10.1rc457.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
653
- qontract_reconcile-0.10.1rc457.dist-info/RECORD,,
649
+ qontract_reconcile-0.10.1rc458.dist-info/METADATA,sha256=dDexn0oD1_4zQL6Fd516zTaG1hPC3S6ASKdXaIHs0qM,2348
650
+ qontract_reconcile-0.10.1rc458.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
651
+ qontract_reconcile-0.10.1rc458.dist-info/entry_points.txt,sha256=rTjAv28I_CHLM8ID3OPqMI_suoQ9s7tFbim4aYjn9kk,376
652
+ qontract_reconcile-0.10.1rc458.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
653
+ qontract_reconcile-0.10.1rc458.dist-info/RECORD,,
@@ -600,7 +600,8 @@ def should_apply(
600
600
  ):
601
601
  ri.register_error()
602
602
  logging.error(
603
- f"[{cluster}/{namespace}] resource '{resource_type}/{name}' present and managed by another caller: {current.caller}"
603
+ f"[{cluster}/{namespace}] resource '{resource_type}/{name}' present and "
604
+ f"managed by another caller: {current.caller}"
604
605
  )
605
606
  return False
606
607
 
@@ -670,6 +671,74 @@ def handle_new_resources(
670
671
  return actions
671
672
 
672
673
 
674
+ def should_take_over(
675
+ current: OR,
676
+ ri: ResourceInventory,
677
+ options: ApplyOptions,
678
+ cluster: str,
679
+ name: str,
680
+ namespace: str,
681
+ resource_type: str,
682
+ ) -> bool:
683
+ if options.caller and options.caller != current.caller:
684
+ # The resources are identical but the caller is different
685
+ if options.take_over or (
686
+ options.all_callers and current.caller not in options.all_callers
687
+ ):
688
+ return True
689
+ else:
690
+ ri.register_error()
691
+ logging.error(
692
+ f"[{cluster}/{namespace}] resource '{resource_type}/{name}' present "
693
+ f" and managed by another caller: {current.caller}"
694
+ )
695
+ return False
696
+
697
+
698
+ def handle_identical_resources(
699
+ oc_map: ClusterMap,
700
+ ri: ResourceInventory,
701
+ identical_resources: Mapping[Any, Any],
702
+ cluster: str,
703
+ namespace: str,
704
+ resource_type: str,
705
+ data: Mapping[Any, Any],
706
+ options: ApplyOptions,
707
+ ) -> list[dict[str, Any]]:
708
+ actions: list[dict[str, Any]] = []
709
+
710
+ for name, dp in identical_resources.items():
711
+ if should_take_over(
712
+ current=dp.current,
713
+ ri=ri,
714
+ cluster=cluster,
715
+ name=name,
716
+ namespace=namespace,
717
+ resource_type=resource_type,
718
+ options=options,
719
+ ):
720
+ options.privileged = data["use_admin_token"].get(name, False)
721
+ action = {
722
+ "action": ACTION_APPLIED,
723
+ "cluster": cluster,
724
+ "namespace": namespace,
725
+ "kind": resource_type,
726
+ "name": name,
727
+ "privileged": options.privileged,
728
+ }
729
+ actions.append(action)
730
+ apply_action(
731
+ oc_map=oc_map,
732
+ ri=ri,
733
+ cluster=cluster,
734
+ namespace=namespace,
735
+ resource_type=resource_type,
736
+ resource=dp.desired,
737
+ options=options,
738
+ )
739
+ return actions
740
+
741
+
673
742
  def handle_modified_resources(
674
743
  oc_map: ClusterMap,
675
744
  ri: ResourceInventory,
@@ -875,6 +944,21 @@ def _realize_resource_data_3way_diff(
875
944
  data["current"], data["desired"], equal=three_way_diff_using_hash
876
945
  )
877
946
 
947
+ # identical resources need to be checked
948
+ # for take_overs and saas file deprecations
949
+ actions.extend(
950
+ handle_identical_resources(
951
+ oc_map=oc_map,
952
+ ri=ri,
953
+ identical_resources=diff_result.identical,
954
+ cluster=cluster,
955
+ namespace=namespace,
956
+ resource_type=resource_type,
957
+ data=data,
958
+ options=options,
959
+ )
960
+ )
961
+
878
962
  actions.extend(
879
963
  handle_new_resources(
880
964
  oc_map=oc_map,
@@ -742,8 +742,8 @@ def apply_options() -> sut.ApplyOptions:
742
742
  recycle_pods=True,
743
743
  take_over=False,
744
744
  override_enable_deletion=False,
745
- caller=None,
746
- all_callers=None,
745
+ caller="saas-test",
746
+ all_callers=["saas-test"],
747
747
  privileged=False,
748
748
  enable_deletion=False,
749
749
  )
@@ -763,9 +763,7 @@ def build_openshift_resource(
763
763
  body = {
764
764
  "kind": kind,
765
765
  "apiVersion": api_version,
766
- "metadata": {
767
- "name": name,
768
- },
766
+ "metadata": {"name": name},
769
767
  }
770
768
  if extra_body:
771
769
  body = body | extra_body
@@ -783,14 +781,22 @@ def build_openshift_resource(
783
781
  def build_openshift_resource_1() -> resource.OpenshiftResource:
784
782
  spec = {"spec": {"test-attr": "test-value-1"}}
785
783
  return build_openshift_resource(
786
- kind="test-kind", api_version="v1", name="test-resource", extra_body=spec
784
+ kind="test-kind",
785
+ api_version="v1",
786
+ name="test-resource",
787
+ extra_body=spec,
788
+ caller_name="saas-test",
787
789
  )
788
790
 
789
791
 
790
792
  def build_openshift_resource_2() -> resource.OpenshiftResource:
791
- spec = {"spec": {"test-attr": "test-value-1"}}
793
+ spec = {"spec": {"test-attr": "test-value-2"}}
792
794
  return build_openshift_resource(
793
- kind="test-kind", api_version="v1", name="test-resource", extra_body=spec
795
+ kind="test-kind",
796
+ api_version="v1",
797
+ name="test-resource",
798
+ extra_body=spec,
799
+ caller_name="saas-test",
794
800
  )
795
801
 
796
802
 
@@ -802,7 +808,7 @@ def diff_result() -> DiffResult:
802
808
  add={"test-resource": r1},
803
809
  change={"test-resource": DiffPair(r1, r2)},
804
810
  delete={"test-resource": r1},
805
- identical={},
811
+ identical={"test-resource": DiffPair(r1, r1)},
806
812
  )
807
813
 
808
814
 
@@ -845,12 +851,86 @@ def test_handle_new_resources(
845
851
  apply_mock.assert_called_with(**apply_expected_args)
846
852
 
847
853
 
854
+ @pytest.mark.parametrize(
855
+ "apply_options, should_apply, should_error_ri",
856
+ [
857
+ ( # Same Caller. The resource should be updated
858
+ sut.ApplyOptions(
859
+ dry_run=True,
860
+ no_dry_run_skip_compare=False,
861
+ wait_for_namespace=True,
862
+ recycle_pods=True,
863
+ take_over=False,
864
+ override_enable_deletion=False,
865
+ caller="saas-test",
866
+ all_callers=["saas-test"],
867
+ privileged=False,
868
+ enable_deletion=False,
869
+ ),
870
+ True,
871
+ False,
872
+ ),
873
+ ( # The resource is owned by "saas-test" but is present in "different".
874
+ # An error must be raised
875
+ sut.ApplyOptions(
876
+ dry_run=True,
877
+ no_dry_run_skip_compare=False,
878
+ wait_for_namespace=True,
879
+ recycle_pods=True,
880
+ take_over=False,
881
+ override_enable_deletion=False,
882
+ caller="different",
883
+ all_callers=["saas-test", "different"],
884
+ privileged=False,
885
+ enable_deletion=False,
886
+ ),
887
+ False,
888
+ True,
889
+ ),
890
+ ( # The Resource is owned by "saas-test" and is being deployed by "different"
891
+ # Since "saas-test" is not in all_callers it means it has been
892
+ # deprecated. The Resource needs to be taken over by "different"
893
+ sut.ApplyOptions(
894
+ dry_run=True,
895
+ no_dry_run_skip_compare=False,
896
+ wait_for_namespace=True,
897
+ recycle_pods=True,
898
+ take_over=False,
899
+ override_enable_deletion=False,
900
+ caller="different",
901
+ all_callers=["different"],
902
+ privileged=False,
903
+ enable_deletion=False,
904
+ ),
905
+ True,
906
+ False,
907
+ ),
908
+ ( # Take over resources from another Saas-file
909
+ sut.ApplyOptions(
910
+ dry_run=True,
911
+ no_dry_run_skip_compare=False,
912
+ wait_for_namespace=True,
913
+ recycle_pods=True,
914
+ take_over=True,
915
+ override_enable_deletion=False,
916
+ caller="different",
917
+ all_callers=["saas-test", "different"],
918
+ privileged=False,
919
+ enable_deletion=False,
920
+ ),
921
+ True,
922
+ False,
923
+ ),
924
+ ],
925
+ )
848
926
  def test_handle_modified_resources(
849
927
  mocker: MockerFixture,
850
928
  oc_map: oc.OC_Map,
851
929
  resource_inventory: resource.ResourceInventory,
852
930
  diff_result: DiffResult,
853
931
  apply_options: sut.ApplyOptions,
932
+ should_apply: bool,
933
+ should_error_ri: bool,
854
934
  ):
855
935
  apply_mock = mocker.patch.object(sut, "apply", autospec=True)
856
936
  cluster = "test-cluster"
@@ -871,19 +951,143 @@ def test_handle_modified_resources(
871
951
  options=apply_options,
872
952
  )
873
953
 
874
- assert len(actions) == 1
875
- apply_expected_args = {
876
- "dry_run": True,
877
- "oc_map": oc_map,
878
- "cluster": "test-cluster",
879
- "namespace": "test-namespace",
880
- "resource_type": "test-Kind",
881
- "resource": diff_result.change["test-resource"].desired,
882
- "wait_for_namespace": True,
883
- "recycle_pods": True,
884
- "privileged": False,
885
- }
886
- apply_mock.assert_called_with(**apply_expected_args)
954
+ if should_apply:
955
+ assert len(actions) == 1
956
+ apply_expected_args = {
957
+ "dry_run": True,
958
+ "oc_map": oc_map,
959
+ "cluster": "test-cluster",
960
+ "namespace": "test-namespace",
961
+ "resource_type": "test-Kind",
962
+ "resource": diff_result.change["test-resource"].desired,
963
+ "wait_for_namespace": True,
964
+ "recycle_pods": True,
965
+ "privileged": False,
966
+ }
967
+ apply_mock.assert_called_with(**apply_expected_args)
968
+ else:
969
+ assert len(actions) == 0
970
+
971
+ if should_error_ri:
972
+ assert resource_inventory._error_registered
973
+
974
+
975
+ @pytest.mark.parametrize(
976
+ "apply_options, should_take_over, should_error_ri",
977
+ [
978
+ ( # Same Caller and Identical Resource. Nothing should happen
979
+ sut.ApplyOptions(
980
+ dry_run=True,
981
+ no_dry_run_skip_compare=False,
982
+ wait_for_namespace=True,
983
+ recycle_pods=True,
984
+ take_over=False,
985
+ override_enable_deletion=False,
986
+ caller="saas-test",
987
+ all_callers=["saas-test"],
988
+ privileged=False,
989
+ enable_deletion=False,
990
+ ),
991
+ False,
992
+ False,
993
+ ),
994
+ ( # The resource is owned by "saas-test" but is present in "different".
995
+ # An error must be raised
996
+ sut.ApplyOptions(
997
+ dry_run=True,
998
+ no_dry_run_skip_compare=False,
999
+ wait_for_namespace=True,
1000
+ recycle_pods=True,
1001
+ take_over=False,
1002
+ override_enable_deletion=False,
1003
+ caller="different",
1004
+ all_callers=["saas-test", "different"],
1005
+ privileged=False,
1006
+ enable_deletion=False,
1007
+ ),
1008
+ False,
1009
+ True,
1010
+ ),
1011
+ ( # The Resource is owned by "saas-test" and is being deployed by "different"
1012
+ # Since "saas-test" is not in all_callers it means it has been
1013
+ # deprecated. The Resource needs to be taken over by "different"
1014
+ sut.ApplyOptions(
1015
+ dry_run=True,
1016
+ no_dry_run_skip_compare=False,
1017
+ wait_for_namespace=True,
1018
+ recycle_pods=True,
1019
+ take_over=False,
1020
+ override_enable_deletion=False,
1021
+ caller="different",
1022
+ all_callers=["different"],
1023
+ privileged=False,
1024
+ enable_deletion=False,
1025
+ ),
1026
+ True,
1027
+ False,
1028
+ ),
1029
+ ( # Take over resources from another Saas-file
1030
+ sut.ApplyOptions(
1031
+ dry_run=True,
1032
+ no_dry_run_skip_compare=False,
1033
+ wait_for_namespace=True,
1034
+ recycle_pods=True,
1035
+ take_over=True,
1036
+ override_enable_deletion=False,
1037
+ caller="different",
1038
+ all_callers=["saas-test", "different"],
1039
+ privileged=False,
1040
+ enable_deletion=False,
1041
+ ),
1042
+ True,
1043
+ False,
1044
+ ),
1045
+ ],
1046
+ )
1047
+ def test_handle_identical_resources(
1048
+ mocker: MockerFixture,
1049
+ oc_map: oc.OC_Map,
1050
+ resource_inventory: resource.ResourceInventory,
1051
+ diff_result: DiffResult,
1052
+ apply_options: sut.ApplyOptions,
1053
+ should_take_over: bool,
1054
+ should_error_ri: bool,
1055
+ ):
1056
+ apply_mock = mocker.patch.object(sut, "apply", autospec=True)
1057
+ cluster = "test-cluster"
1058
+ namespace = "test-namespace"
1059
+ resource_type = "test-Kind"
1060
+ data = {"use_admin_token": {"test-resource": False}}
1061
+
1062
+ actions = sut.handle_identical_resources(
1063
+ oc_map=oc_map,
1064
+ ri=resource_inventory,
1065
+ identical_resources=diff_result.identical,
1066
+ cluster=cluster,
1067
+ namespace=namespace,
1068
+ resource_type=resource_type,
1069
+ data=data,
1070
+ options=apply_options,
1071
+ )
1072
+
1073
+ if should_take_over:
1074
+ assert len(actions) == 1
1075
+ apply_expected_args = {
1076
+ "dry_run": True,
1077
+ "oc_map": oc_map,
1078
+ "cluster": "test-cluster",
1079
+ "namespace": "test-namespace",
1080
+ "resource_type": "test-Kind",
1081
+ "resource": diff_result.identical["test-resource"].desired,
1082
+ "wait_for_namespace": True,
1083
+ "recycle_pods": True,
1084
+ "privileged": False,
1085
+ }
1086
+ apply_mock.assert_called_with(**apply_expected_args)
1087
+ else:
1088
+ assert len(actions) == 0
1089
+ if should_error_ri:
1090
+ assert resource_inventory._error_registered
887
1091
 
888
1092
 
889
1093
  def test_handle_deleted_resources(