qontract-reconcile 0.10.2.dev178__py3-none-any.whl → 0.10.2.dev180__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.
- {qontract_reconcile-0.10.2.dev178.dist-info → qontract_reconcile-0.10.2.dev180.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.2.dev178.dist-info → qontract_reconcile-0.10.2.dev180.dist-info}/RECORD +14 -14
- reconcile/aus/base.py +17 -3
- reconcile/aus/metrics.py +12 -0
- reconcile/aus/ocm_addons_upgrade_scheduler_org.py +1 -0
- reconcile/aus/ocm_upgrade_scheduler.py +5 -1
- reconcile/gql_definitions/acs/acs_policies.py +15 -7
- reconcile/gql_definitions/introspection.json +210 -148
- reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py +10 -2
- reconcile/jira_permissions_validator.py +68 -43
- reconcile/utils/acs/notifiers.py +4 -2
- reconcile/utils/jira_client.py +99 -14
- {qontract_reconcile-0.10.2.dev178.dist-info → qontract_reconcile-0.10.2.dev180.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev178.dist-info → qontract_reconcile-0.10.2.dev180.dist-info}/entry_points.txt +0 -0
{qontract_reconcile-0.10.2.dev178.dist-info → qontract_reconcile-0.10.2.dev180.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: qontract-reconcile
|
3
|
-
Version: 0.10.2.
|
3
|
+
Version: 0.10.2.dev180
|
4
4
|
Summary: Collection of tools to reconcile services with their desired state as defined in the app-interface DB.
|
5
5
|
Project-URL: homepage, https://github.com/app-sre/qontract-reconcile
|
6
6
|
Project-URL: repository, https://github.com/app-sre/qontract-reconcile
|
{qontract_reconcile-0.10.2.dev178.dist-info → qontract_reconcile-0.10.2.dev180.dist-info}/RECORD
RENAMED
@@ -46,7 +46,7 @@ reconcile/jenkins_roles.py,sha256=HadmoNhgOoKMFZJmCe_Gdg0_a9k_1MuOXidtr801nUU,45
|
|
46
46
|
reconcile/jenkins_webhooks.py,sha256=dzMT1ywXjeAo5sHj-ittW06Ed3beAUPjnc_oCAtD-Rg,2150
|
47
47
|
reconcile/jenkins_webhooks_cleaner.py,sha256=JsN_NVPfZJwv1JtSzZXDIHUqGiefL-DRffFnDGau9aY,1539
|
48
48
|
reconcile/jenkins_worker_fleets.py,sha256=L2wEXpd4xuEHrXGss4iH788nG8UlLSYduZe1EY2IVw4,5377
|
49
|
-
reconcile/jira_permissions_validator.py,sha256=
|
49
|
+
reconcile/jira_permissions_validator.py,sha256=5rc4Q2mXGL3HCZmYpZaJkjzBrpCRnlLeCY0Yl2fDOs4,14672
|
50
50
|
reconcile/jira_watcher.py,sha256=L_UL2MKm2SoIGNsCLThm28pnqCkoFc154JWsD6bURug,3593
|
51
51
|
reconcile/ldap_users.py,sha256=7hdO5CAPl-VNBvDRmKHg13LoblHXXPt7YEKNGomAoGg,3158
|
52
52
|
reconcile/mr_client_gateway.py,sha256=WhjMd-sIXDFCV8-rt8CEjurJ5OYB1pOD0K3o0tZRXQg,1885
|
@@ -123,14 +123,14 @@ reconcile/vpc_peerings_validator.py,sha256=aESqrhm1tpkc2iqSL1UV5to_HjNgjRSffD0cr
|
|
123
123
|
reconcile/aus/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
124
124
|
reconcile/aus/advanced_upgrade_service.py,sha256=lt684trHbKvVDLwwuNVz3Wu_MnytFSbS_7MZTIITh9k,23969
|
125
125
|
reconcile/aus/aus_label_source.py,sha256=o0S2f0qwcII_8nzhHZhRQ83gEZ1DrSXyO4xzSwLebuU,4382
|
126
|
-
reconcile/aus/base.py,sha256=
|
126
|
+
reconcile/aus/base.py,sha256=Kicj39qfAlINywfk0Legu7RUcUxI4AuoBhA8vJ1gWag,51343
|
127
127
|
reconcile/aus/cluster_version_data.py,sha256=VZWbUEIbrDKO-sroMpQtiWCTqDraTMd8tssKV0HyTQ0,7140
|
128
128
|
reconcile/aus/healthchecks.py,sha256=jR9c-syh9impnkV0fd6XW3Bnk7iRN5zv8oCRYM-yIRY,2700
|
129
|
-
reconcile/aus/metrics.py,sha256=
|
129
|
+
reconcile/aus/metrics.py,sha256=qh3-oWL8-Hbj1uXgAKonw7sVXGBlPpCyHtVYJusZ9n8,4271
|
130
130
|
reconcile/aus/models.py,sha256=qLjWLDJe5PGXPPtJ5PI01IVEYaSGweu9dkAgf0ZM2hk,8297
|
131
131
|
reconcile/aus/node_pool_spec.py,sha256=FkMggklG-4BgQwud2Swp2m3AAAKzZmeaXgohl9uwxZ8,1138
|
132
|
-
reconcile/aus/ocm_addons_upgrade_scheduler_org.py,sha256=
|
133
|
-
reconcile/aus/ocm_upgrade_scheduler.py,sha256=
|
132
|
+
reconcile/aus/ocm_addons_upgrade_scheduler_org.py,sha256=PL8QdzWh8lhGneMwSbIYxOp002mJd5gHf_T0Q2cWQho,10350
|
133
|
+
reconcile/aus/ocm_upgrade_scheduler.py,sha256=WPIUUr3n3j-ZtyE3DgsPKzBJ3Dm9i1PSHBRepAf6CF4,3783
|
134
134
|
reconcile/aus/ocm_upgrade_scheduler_org.py,sha256=QeZAQ1wdDPcwZ77b3Xmt4yBV60rWi3qd8h-vGwnwoCs,3948
|
135
135
|
reconcile/aus/upgrades.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
136
136
|
reconcile/aus/version_gate_approver.py,sha256=iZg7l-VgcgyVf7jV9mLZzXq3d22r8feAnl-1LDVUJR4,7358
|
@@ -227,10 +227,10 @@ reconcile/glitchtip_project_alerts/integration.py,sha256=BgMx-NyV9mTuv7Sotb2OioC
|
|
227
227
|
reconcile/glitchtip_project_dsn/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
228
228
|
reconcile/glitchtip_project_dsn/integration.py,sha256=2iugub-kHYkHNK33n0v9_TeWonuxCPah_VkoTPvaajE,8077
|
229
229
|
reconcile/gql_definitions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
230
|
-
reconcile/gql_definitions/introspection.json,sha256=
|
230
|
+
reconcile/gql_definitions/introspection.json,sha256=lgoJW52hU7zL4UKNpKywvyL--o36MggUI1eFWWlvYLk,2300074
|
231
231
|
reconcile/gql_definitions/acs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
232
232
|
reconcile/gql_definitions/acs/acs_instances.py,sha256=L91WW9LbhJbBSrECqShQpFtjoBOsmNIYLRpMbx1io5o,2181
|
233
|
-
reconcile/gql_definitions/acs/acs_policies.py,sha256=
|
233
|
+
reconcile/gql_definitions/acs/acs_policies.py,sha256=9IFpGAcrZ8nTFC05q3-12nwkNOEkquHB2-XbVq5_jy4,7219
|
234
234
|
reconcile/gql_definitions/acs/acs_rbac.py,sha256=cZsIlCWliPQdQHgmBsIMx54fJNOtkdRXLzmOKZmJNHk,3009
|
235
235
|
reconcile/gql_definitions/advanced_upgrade_service/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
236
236
|
reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py,sha256=230uwBoBaaVz686d_WNZT9eAw6BX2VpoQX0mKhf5UcM,4390
|
@@ -376,7 +376,7 @@ reconcile/gql_definitions/jenkins_configs/jenkins_instances.py,sha256=b3gYVzQavx
|
|
376
376
|
reconcile/gql_definitions/jira/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
377
377
|
reconcile/gql_definitions/jira/jira_servers.py,sha256=N7mvIdqoXT-90abkiaC2bxz2ZjW3d826qVV5OL8_TAM,2223
|
378
378
|
reconcile/gql_definitions/jira_permissions_validator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
379
|
-
reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py,sha256=
|
379
|
+
reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py,sha256=RqIDe90sKjEfFNEIxtfBg10Bddc11M6I7ae_FqBl3To,4034
|
380
380
|
reconcile/gql_definitions/jumphosts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
381
381
|
reconcile/gql_definitions/jumphosts/jumphosts.py,sha256=gN595lx7K1XsB2AfxDQ911TBVBbCoxibVeujnsGue_Q,2371
|
382
382
|
reconcile/gql_definitions/ldap_groups/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -625,7 +625,7 @@ reconcile/utils/helpers.py,sha256=womAD2bKPUAFOjHvNPAe_2Hsb-oVTxuQiYPGeR-Thp0,17
|
|
625
625
|
reconcile/utils/imap_client.py,sha256=h8YDiCSCvroErhpH_-KGYI7Y2WU2Q2oSpuxDFbOkSbY,1989
|
626
626
|
reconcile/utils/instrumented_wrappers.py,sha256=VqT4s0Bdicv224-uSeSaugtHXm-xJ3oSeBiqj0QQRiU,1942
|
627
627
|
reconcile/utils/jenkins_api.py,sha256=RaKuZmO7_lbI-hE6c_Pq2a6CQdmBVj7BcP2jR68cIbI,7081
|
628
|
-
reconcile/utils/jira_client.py,sha256=
|
628
|
+
reconcile/utils/jira_client.py,sha256=xhklHRsMnQqZ8WCxO1efjRKvx6diLcGQ6DLi5ZKl_HM,10544
|
629
629
|
reconcile/utils/jjb_client.py,sha256=4YqeXEkO4p6QtJE_fkaD1XuLKbe9l3g0W7AVpcjJ3yg,15187
|
630
630
|
reconcile/utils/jsonpath.py,sha256=wdxOMqR-GMpQf5vRPWRMqAF7bCiXDBkkcFfY2U4j_tk,5536
|
631
631
|
reconcile/utils/jump_host.py,sha256=gi8vGUDgdTVwJvROvRVauFxtL0YAramhbWvG70L7AY8,5137
|
@@ -675,7 +675,7 @@ reconcile/utils/vaultsecretref.py,sha256=0KUSzuvTRxPyKY919TO3-B_eYg4_76fzKvMF8j5
|
|
675
675
|
reconcile/utils/vcs.py,sha256=YtjR3E_VaS7lA4ROzSr7hte-8G-SZ_UmJP5RJ2DCyJk,8807
|
676
676
|
reconcile/utils/acs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
677
677
|
reconcile/utils/acs/base.py,sha256=4UsDrCpAOuddL3PKNuIQYoJP1BtZQNNB8_KEX0lXneg,2532
|
678
|
-
reconcile/utils/acs/notifiers.py,sha256=
|
678
|
+
reconcile/utils/acs/notifiers.py,sha256=DlzTDM9arWQlBSiDy70y5Mf38OKVs9V0FzFe2LfOKXA,5046
|
679
679
|
reconcile/utils/acs/policies.py,sha256=jpbi3qpGkBD_X6MfzsX12dPajUbmACmhIOz_0rDvYzs,5489
|
680
680
|
reconcile/utils/acs/rbac.py,sha256=ugsLM9Pb7FbUbdq85E3VzXGMaB9ZovXob7tdWCxwqZ8,8808
|
681
681
|
reconcile/utils/aws_api_typed/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -807,7 +807,7 @@ tools/saas_promotion_state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
|
|
807
807
|
tools/saas_promotion_state/saas_promotion_state.py,sha256=UfwwRLS5Ya4_Nh1w5n1dvoYtchQvYE9yj1VANt2IKqI,3925
|
808
808
|
tools/sre_checkpoints/__init__.py,sha256=CDaDaywJnmRCLyl_NCcvxi-Zc0hTi_3OdwKiFOyS39I,145
|
809
809
|
tools/sre_checkpoints/util.py,sha256=zEDbGr18ZeHNQwW8pUsr2JRjuXIPz--WAGJxZo9sv_Y,894
|
810
|
-
qontract_reconcile-0.10.2.
|
811
|
-
qontract_reconcile-0.10.2.
|
812
|
-
qontract_reconcile-0.10.2.
|
813
|
-
qontract_reconcile-0.10.2.
|
810
|
+
qontract_reconcile-0.10.2.dev180.dist-info/METADATA,sha256=3ABcj9gi3YuKUyzROkTB3i4Mo6F6eXVi4BpgPXIwLg4,24627
|
811
|
+
qontract_reconcile-0.10.2.dev180.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
812
|
+
qontract_reconcile-0.10.2.dev180.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
|
813
|
+
qontract_reconcile-0.10.2.dev180.dist-info/RECORD,,
|
reconcile/aus/base.py
CHANGED
@@ -35,6 +35,7 @@ from reconcile.aus.metrics import (
|
|
35
35
|
UPGRADE_SCHEDULED_METRIC_VALUE,
|
36
36
|
UPGRADE_STARTED_METRIC_VALUE,
|
37
37
|
AUSClusterHealthStateGauge,
|
38
|
+
AUSClusterMissingVersionGateAgreementsGauge,
|
38
39
|
AUSClusterUpgradePolicyInfoMetric,
|
39
40
|
AUSOCMEnvironmentError,
|
40
41
|
AUSOrganizationErrorRate,
|
@@ -1066,6 +1067,7 @@ def calculate_diff(
|
|
1066
1067
|
ocm_api: OCMBaseClient,
|
1067
1068
|
version_data: VersionData,
|
1068
1069
|
addon_id: str = "",
|
1070
|
+
integration: str = "",
|
1069
1071
|
) -> list[UpgradePolicyHandler]:
|
1070
1072
|
"""Check available upgrades for each cluster in the desired state
|
1071
1073
|
according to upgrade conditions
|
@@ -1169,13 +1171,25 @@ def calculate_diff(
|
|
1169
1171
|
},
|
1170
1172
|
)
|
1171
1173
|
if gates_with_missing_agreements:
|
1172
|
-
|
1173
|
-
gate.
|
1174
|
+
missing_gate_labels = [
|
1175
|
+
gate.label for gate in gates_with_missing_agreements
|
1174
1176
|
]
|
1175
1177
|
logging.info(
|
1176
|
-
f"[{spec.org.org_id}/{spec.org.name}/{spec.cluster.name}] found gates with missing agreements for {target_version_prefix} - {
|
1178
|
+
f"[{spec.org.org_id}/{spec.org.name}/{spec.cluster.name}] found gates with missing agreements for {target_version_prefix} - {missing_gate_labels} "
|
1177
1179
|
"Skip creation of an upgrade policy until all of them have been acked by the version-gate-approver integration or a user."
|
1178
1180
|
)
|
1181
|
+
|
1182
|
+
metrics.set_gauge(
|
1183
|
+
AUSClusterMissingVersionGateAgreementsGauge(
|
1184
|
+
integration=integration,
|
1185
|
+
ocm_env=spec.org.environment.name,
|
1186
|
+
org_id=spec.org.org_id,
|
1187
|
+
cluster_uuid=spec.cluster.id,
|
1188
|
+
version_prefix=target_version_prefix,
|
1189
|
+
),
|
1190
|
+
len(gates_with_missing_agreements),
|
1191
|
+
)
|
1192
|
+
|
1179
1193
|
continue
|
1180
1194
|
diffs.append(
|
1181
1195
|
UpgradePolicyHandler(
|
reconcile/aus/metrics.py
CHANGED
@@ -56,6 +56,18 @@ class AUSClusterHealthStateGauge(AUSBaseMetric, GaugeMetric):
|
|
56
56
|
return "aus_cluster_health_state"
|
57
57
|
|
58
58
|
|
59
|
+
class AUSClusterMissingVersionGateAgreementsGauge(AUSBaseMetric, GaugeMetric):
|
60
|
+
"The number of missing version gate agreements for a cluster and a target version prefix."
|
61
|
+
|
62
|
+
org_id: str
|
63
|
+
cluster_uuid: str
|
64
|
+
version_prefix: str
|
65
|
+
|
66
|
+
@classmethod
|
67
|
+
def name(cls) -> str:
|
68
|
+
return "aus_cluster_missing_version_gate_agreements"
|
69
|
+
|
70
|
+
|
59
71
|
class AUSAddonVersionRemainingSoakDaysGauge(AUSClusterVersionRemainingSoakDaysGauge):
|
60
72
|
"Remaining days a version needs to soak for an addon on a cluster"
|
61
73
|
|
@@ -76,7 +76,11 @@ class OCMClusterUpgradeSchedulerIntegration(
|
|
76
76
|
)
|
77
77
|
|
78
78
|
diffs = aus.calculate_diff(
|
79
|
-
current_state,
|
79
|
+
current_state,
|
80
|
+
org_upgrade_spec,
|
81
|
+
ocm_api,
|
82
|
+
version_data,
|
83
|
+
integration=self.name,
|
80
84
|
)
|
81
85
|
aus.act(dry_run, diffs, ocm_api)
|
82
86
|
|
@@ -21,7 +21,7 @@ from pydantic import ( # noqa: F401 # pylint: disable=W0611
|
|
21
21
|
DEFINITION = """
|
22
22
|
query AcsPolicy {
|
23
23
|
acs_policies: acs_policy_v1 {
|
24
|
-
|
24
|
+
name
|
25
25
|
description
|
26
26
|
severity
|
27
27
|
integrations {
|
@@ -43,7 +43,10 @@ query AcsPolicy {
|
|
43
43
|
}
|
44
44
|
}
|
45
45
|
issueType
|
46
|
-
|
46
|
+
issueFields {
|
47
|
+
name
|
48
|
+
value
|
49
|
+
}
|
47
50
|
disable {
|
48
51
|
integrations
|
49
52
|
}
|
@@ -59,12 +62,12 @@ query AcsPolicy {
|
|
59
62
|
scope {
|
60
63
|
level
|
61
64
|
... on AcsPolicyScopeCluster_v1 {
|
62
|
-
|
65
|
+
clusters {
|
63
66
|
name
|
64
|
-
}
|
67
|
+
}
|
65
68
|
}
|
66
69
|
... on AcsPolicyScopeNamespace_v1 {
|
67
|
-
|
70
|
+
namespaces {
|
68
71
|
name
|
69
72
|
cluster {
|
70
73
|
name
|
@@ -72,7 +75,7 @@ query AcsPolicy {
|
|
72
75
|
}
|
73
76
|
}
|
74
77
|
}
|
75
|
-
|
78
|
+
conditions {
|
76
79
|
policyField
|
77
80
|
... on AcsPolicyConditionsCvss_v1 {
|
78
81
|
comparison
|
@@ -118,6 +121,11 @@ class JiraSeverityPriorityMappingsV1(ConfiguredBaseModel):
|
|
118
121
|
mappings: list[SeverityPriorityMappingV1] = Field(..., alias="mappings")
|
119
122
|
|
120
123
|
|
124
|
+
class JiraBoardIssueFieldV1(ConfiguredBaseModel):
|
125
|
+
name: str = Field(..., alias="name")
|
126
|
+
value: str = Field(..., alias="value")
|
127
|
+
|
128
|
+
|
121
129
|
class DisableJiraBoardAutomationsV1(ConfiguredBaseModel):
|
122
130
|
integrations: Optional[list[str]] = Field(..., alias="integrations")
|
123
131
|
|
@@ -127,7 +135,7 @@ class JiraBoardV1(ConfiguredBaseModel):
|
|
127
135
|
server: JiraServerV1 = Field(..., alias="server")
|
128
136
|
severity_priority_mappings: JiraSeverityPriorityMappingsV1 = Field(..., alias="severityPriorityMappings")
|
129
137
|
issue_type: Optional[str] = Field(..., alias="issueType")
|
130
|
-
|
138
|
+
issue_fields: Optional[list[JiraBoardIssueFieldV1]] = Field(..., alias="issueFields")
|
131
139
|
disable: Optional[DisableJiraBoardAutomationsV1] = Field(..., alias="disable")
|
132
140
|
|
133
141
|
|
@@ -5440,6 +5440,11 @@
|
|
5440
5440
|
"name": "MembershipProvider_V1",
|
5441
5441
|
"ofType": null
|
5442
5442
|
},
|
5443
|
+
{
|
5444
|
+
"kind": "OBJECT",
|
5445
|
+
"name": "ExternalUser_v1",
|
5446
|
+
"ofType": null
|
5447
|
+
},
|
5443
5448
|
{
|
5444
5449
|
"kind": "OBJECT",
|
5445
5450
|
"name": "GabiInstance_v1",
|
@@ -13830,6 +13835,151 @@
|
|
13830
13835
|
"enumValues": null,
|
13831
13836
|
"possibleTypes": null
|
13832
13837
|
},
|
13838
|
+
{
|
13839
|
+
"kind": "OBJECT",
|
13840
|
+
"name": "ExternalUser_v1",
|
13841
|
+
"description": null,
|
13842
|
+
"fields": [
|
13843
|
+
{
|
13844
|
+
"name": "schema",
|
13845
|
+
"description": null,
|
13846
|
+
"args": [],
|
13847
|
+
"type": {
|
13848
|
+
"kind": "NON_NULL",
|
13849
|
+
"name": null,
|
13850
|
+
"ofType": {
|
13851
|
+
"kind": "SCALAR",
|
13852
|
+
"name": "String",
|
13853
|
+
"ofType": null
|
13854
|
+
}
|
13855
|
+
},
|
13856
|
+
"isDeprecated": false,
|
13857
|
+
"deprecationReason": null
|
13858
|
+
},
|
13859
|
+
{
|
13860
|
+
"name": "path",
|
13861
|
+
"description": null,
|
13862
|
+
"args": [],
|
13863
|
+
"type": {
|
13864
|
+
"kind": "NON_NULL",
|
13865
|
+
"name": null,
|
13866
|
+
"ofType": {
|
13867
|
+
"kind": "SCALAR",
|
13868
|
+
"name": "String",
|
13869
|
+
"ofType": null
|
13870
|
+
}
|
13871
|
+
},
|
13872
|
+
"isDeprecated": false,
|
13873
|
+
"deprecationReason": null
|
13874
|
+
},
|
13875
|
+
{
|
13876
|
+
"name": "labels",
|
13877
|
+
"description": null,
|
13878
|
+
"args": [],
|
13879
|
+
"type": {
|
13880
|
+
"kind": "SCALAR",
|
13881
|
+
"name": "JSON",
|
13882
|
+
"ofType": null
|
13883
|
+
},
|
13884
|
+
"isDeprecated": false,
|
13885
|
+
"deprecationReason": null
|
13886
|
+
},
|
13887
|
+
{
|
13888
|
+
"name": "name",
|
13889
|
+
"description": null,
|
13890
|
+
"args": [],
|
13891
|
+
"type": {
|
13892
|
+
"kind": "NON_NULL",
|
13893
|
+
"name": null,
|
13894
|
+
"ofType": {
|
13895
|
+
"kind": "SCALAR",
|
13896
|
+
"name": "String",
|
13897
|
+
"ofType": null
|
13898
|
+
}
|
13899
|
+
},
|
13900
|
+
"isDeprecated": false,
|
13901
|
+
"deprecationReason": null
|
13902
|
+
},
|
13903
|
+
{
|
13904
|
+
"name": "github_username",
|
13905
|
+
"description": null,
|
13906
|
+
"args": [],
|
13907
|
+
"type": {
|
13908
|
+
"kind": "SCALAR",
|
13909
|
+
"name": "String",
|
13910
|
+
"ofType": null
|
13911
|
+
},
|
13912
|
+
"isDeprecated": false,
|
13913
|
+
"deprecationReason": null
|
13914
|
+
},
|
13915
|
+
{
|
13916
|
+
"name": "quay_username",
|
13917
|
+
"description": null,
|
13918
|
+
"args": [],
|
13919
|
+
"type": {
|
13920
|
+
"kind": "SCALAR",
|
13921
|
+
"name": "String",
|
13922
|
+
"ofType": null
|
13923
|
+
},
|
13924
|
+
"isDeprecated": false,
|
13925
|
+
"deprecationReason": null
|
13926
|
+
},
|
13927
|
+
{
|
13928
|
+
"name": "sponsors",
|
13929
|
+
"description": null,
|
13930
|
+
"args": [],
|
13931
|
+
"type": {
|
13932
|
+
"kind": "NON_NULL",
|
13933
|
+
"name": null,
|
13934
|
+
"ofType": {
|
13935
|
+
"kind": "LIST",
|
13936
|
+
"name": null,
|
13937
|
+
"ofType": {
|
13938
|
+
"kind": "NON_NULL",
|
13939
|
+
"name": null,
|
13940
|
+
"ofType": {
|
13941
|
+
"kind": "OBJECT",
|
13942
|
+
"name": "User_v1",
|
13943
|
+
"ofType": null
|
13944
|
+
}
|
13945
|
+
}
|
13946
|
+
}
|
13947
|
+
},
|
13948
|
+
"isDeprecated": false,
|
13949
|
+
"deprecationReason": null
|
13950
|
+
},
|
13951
|
+
{
|
13952
|
+
"name": "roles",
|
13953
|
+
"description": null,
|
13954
|
+
"args": [],
|
13955
|
+
"type": {
|
13956
|
+
"kind": "LIST",
|
13957
|
+
"name": null,
|
13958
|
+
"ofType": {
|
13959
|
+
"kind": "NON_NULL",
|
13960
|
+
"name": null,
|
13961
|
+
"ofType": {
|
13962
|
+
"kind": "OBJECT",
|
13963
|
+
"name": "Role_v1",
|
13964
|
+
"ofType": null
|
13965
|
+
}
|
13966
|
+
}
|
13967
|
+
},
|
13968
|
+
"isDeprecated": false,
|
13969
|
+
"deprecationReason": null
|
13970
|
+
}
|
13971
|
+
],
|
13972
|
+
"inputFields": null,
|
13973
|
+
"interfaces": [
|
13974
|
+
{
|
13975
|
+
"kind": "INTERFACE",
|
13976
|
+
"name": "DatafileObject_v1",
|
13977
|
+
"ofType": null
|
13978
|
+
}
|
13979
|
+
],
|
13980
|
+
"enumValues": null,
|
13981
|
+
"possibleTypes": null
|
13982
|
+
},
|
13833
13983
|
{
|
13834
13984
|
"kind": "OBJECT",
|
13835
13985
|
"name": "CredentialsRequest_v1",
|
@@ -19225,7 +19375,7 @@
|
|
19225
19375
|
"deprecationReason": null
|
19226
19376
|
},
|
19227
19377
|
{
|
19228
|
-
"name": "
|
19378
|
+
"name": "wontFixResolution",
|
19229
19379
|
"description": null,
|
19230
19380
|
"args": [],
|
19231
19381
|
"type": {
|
@@ -19237,7 +19387,7 @@
|
|
19237
19387
|
"deprecationReason": null
|
19238
19388
|
},
|
19239
19389
|
{
|
19240
|
-
"name": "
|
19390
|
+
"name": "reopenDuration",
|
19241
19391
|
"description": null,
|
19242
19392
|
"args": [],
|
19243
19393
|
"type": {
|
@@ -19249,25 +19399,33 @@
|
|
19249
19399
|
"deprecationReason": null
|
19250
19400
|
},
|
19251
19401
|
{
|
19252
|
-
"name": "
|
19402
|
+
"name": "disable",
|
19253
19403
|
"description": null,
|
19254
19404
|
"args": [],
|
19255
19405
|
"type": {
|
19256
|
-
"kind": "
|
19257
|
-
"name": "
|
19406
|
+
"kind": "OBJECT",
|
19407
|
+
"name": "DisableJiraBoardAutomations_v1",
|
19258
19408
|
"ofType": null
|
19259
19409
|
},
|
19260
19410
|
"isDeprecated": false,
|
19261
19411
|
"deprecationReason": null
|
19262
19412
|
},
|
19263
19413
|
{
|
19264
|
-
"name": "
|
19414
|
+
"name": "issueFields",
|
19265
19415
|
"description": null,
|
19266
19416
|
"args": [],
|
19267
19417
|
"type": {
|
19268
|
-
"kind": "
|
19269
|
-
"name":
|
19270
|
-
"ofType":
|
19418
|
+
"kind": "LIST",
|
19419
|
+
"name": null,
|
19420
|
+
"ofType": {
|
19421
|
+
"kind": "NON_NULL",
|
19422
|
+
"name": null,
|
19423
|
+
"ofType": {
|
19424
|
+
"kind": "OBJECT",
|
19425
|
+
"name": "JiraBoardIssueField_v1",
|
19426
|
+
"ofType": null
|
19427
|
+
}
|
19428
|
+
}
|
19271
19429
|
},
|
19272
19430
|
"isDeprecated": false,
|
19273
19431
|
"deprecationReason": null
|
@@ -19734,6 +19892,49 @@
|
|
19734
19892
|
"enumValues": null,
|
19735
19893
|
"possibleTypes": null
|
19736
19894
|
},
|
19895
|
+
{
|
19896
|
+
"kind": "OBJECT",
|
19897
|
+
"name": "JiraBoardIssueField_v1",
|
19898
|
+
"description": null,
|
19899
|
+
"fields": [
|
19900
|
+
{
|
19901
|
+
"name": "name",
|
19902
|
+
"description": null,
|
19903
|
+
"args": [],
|
19904
|
+
"type": {
|
19905
|
+
"kind": "NON_NULL",
|
19906
|
+
"name": null,
|
19907
|
+
"ofType": {
|
19908
|
+
"kind": "SCALAR",
|
19909
|
+
"name": "String",
|
19910
|
+
"ofType": null
|
19911
|
+
}
|
19912
|
+
},
|
19913
|
+
"isDeprecated": false,
|
19914
|
+
"deprecationReason": null
|
19915
|
+
},
|
19916
|
+
{
|
19917
|
+
"name": "value",
|
19918
|
+
"description": null,
|
19919
|
+
"args": [],
|
19920
|
+
"type": {
|
19921
|
+
"kind": "NON_NULL",
|
19922
|
+
"name": null,
|
19923
|
+
"ofType": {
|
19924
|
+
"kind": "SCALAR",
|
19925
|
+
"name": "String",
|
19926
|
+
"ofType": null
|
19927
|
+
}
|
19928
|
+
},
|
19929
|
+
"isDeprecated": false,
|
19930
|
+
"deprecationReason": null
|
19931
|
+
}
|
19932
|
+
],
|
19933
|
+
"inputFields": null,
|
19934
|
+
"interfaces": [],
|
19935
|
+
"enumValues": null,
|
19936
|
+
"possibleTypes": null
|
19937
|
+
},
|
19737
19938
|
{
|
19738
19939
|
"kind": "OBJECT",
|
19739
19940
|
"name": "AcsPolicy_v1",
|
@@ -27249,145 +27450,6 @@
|
|
27249
27450
|
}
|
27250
27451
|
]
|
27251
27452
|
},
|
27252
|
-
{
|
27253
|
-
"kind": "OBJECT",
|
27254
|
-
"name": "ExternalUser_v1",
|
27255
|
-
"description": null,
|
27256
|
-
"fields": [
|
27257
|
-
{
|
27258
|
-
"name": "schema",
|
27259
|
-
"description": null,
|
27260
|
-
"args": [],
|
27261
|
-
"type": {
|
27262
|
-
"kind": "NON_NULL",
|
27263
|
-
"name": null,
|
27264
|
-
"ofType": {
|
27265
|
-
"kind": "SCALAR",
|
27266
|
-
"name": "String",
|
27267
|
-
"ofType": null
|
27268
|
-
}
|
27269
|
-
},
|
27270
|
-
"isDeprecated": false,
|
27271
|
-
"deprecationReason": null
|
27272
|
-
},
|
27273
|
-
{
|
27274
|
-
"name": "path",
|
27275
|
-
"description": null,
|
27276
|
-
"args": [],
|
27277
|
-
"type": {
|
27278
|
-
"kind": "NON_NULL",
|
27279
|
-
"name": null,
|
27280
|
-
"ofType": {
|
27281
|
-
"kind": "SCALAR",
|
27282
|
-
"name": "String",
|
27283
|
-
"ofType": null
|
27284
|
-
}
|
27285
|
-
},
|
27286
|
-
"isDeprecated": false,
|
27287
|
-
"deprecationReason": null
|
27288
|
-
},
|
27289
|
-
{
|
27290
|
-
"name": "labels",
|
27291
|
-
"description": null,
|
27292
|
-
"args": [],
|
27293
|
-
"type": {
|
27294
|
-
"kind": "SCALAR",
|
27295
|
-
"name": "JSON",
|
27296
|
-
"ofType": null
|
27297
|
-
},
|
27298
|
-
"isDeprecated": false,
|
27299
|
-
"deprecationReason": null
|
27300
|
-
},
|
27301
|
-
{
|
27302
|
-
"name": "name",
|
27303
|
-
"description": null,
|
27304
|
-
"args": [],
|
27305
|
-
"type": {
|
27306
|
-
"kind": "NON_NULL",
|
27307
|
-
"name": null,
|
27308
|
-
"ofType": {
|
27309
|
-
"kind": "SCALAR",
|
27310
|
-
"name": "String",
|
27311
|
-
"ofType": null
|
27312
|
-
}
|
27313
|
-
},
|
27314
|
-
"isDeprecated": false,
|
27315
|
-
"deprecationReason": null
|
27316
|
-
},
|
27317
|
-
{
|
27318
|
-
"name": "github_username",
|
27319
|
-
"description": null,
|
27320
|
-
"args": [],
|
27321
|
-
"type": {
|
27322
|
-
"kind": "SCALAR",
|
27323
|
-
"name": "String",
|
27324
|
-
"ofType": null
|
27325
|
-
},
|
27326
|
-
"isDeprecated": false,
|
27327
|
-
"deprecationReason": null
|
27328
|
-
},
|
27329
|
-
{
|
27330
|
-
"name": "quay_username",
|
27331
|
-
"description": null,
|
27332
|
-
"args": [],
|
27333
|
-
"type": {
|
27334
|
-
"kind": "SCALAR",
|
27335
|
-
"name": "String",
|
27336
|
-
"ofType": null
|
27337
|
-
},
|
27338
|
-
"isDeprecated": false,
|
27339
|
-
"deprecationReason": null
|
27340
|
-
},
|
27341
|
-
{
|
27342
|
-
"name": "sponsors",
|
27343
|
-
"description": null,
|
27344
|
-
"args": [],
|
27345
|
-
"type": {
|
27346
|
-
"kind": "NON_NULL",
|
27347
|
-
"name": null,
|
27348
|
-
"ofType": {
|
27349
|
-
"kind": "LIST",
|
27350
|
-
"name": null,
|
27351
|
-
"ofType": {
|
27352
|
-
"kind": "NON_NULL",
|
27353
|
-
"name": null,
|
27354
|
-
"ofType": {
|
27355
|
-
"kind": "OBJECT",
|
27356
|
-
"name": "User_v1",
|
27357
|
-
"ofType": null
|
27358
|
-
}
|
27359
|
-
}
|
27360
|
-
}
|
27361
|
-
},
|
27362
|
-
"isDeprecated": false,
|
27363
|
-
"deprecationReason": null
|
27364
|
-
},
|
27365
|
-
{
|
27366
|
-
"name": "roles",
|
27367
|
-
"description": null,
|
27368
|
-
"args": [],
|
27369
|
-
"type": {
|
27370
|
-
"kind": "LIST",
|
27371
|
-
"name": null,
|
27372
|
-
"ofType": {
|
27373
|
-
"kind": "NON_NULL",
|
27374
|
-
"name": null,
|
27375
|
-
"ofType": {
|
27376
|
-
"kind": "OBJECT",
|
27377
|
-
"name": "Role_v1",
|
27378
|
-
"ofType": null
|
27379
|
-
}
|
27380
|
-
}
|
27381
|
-
},
|
27382
|
-
"isDeprecated": false,
|
27383
|
-
"deprecationReason": null
|
27384
|
-
}
|
27385
|
-
],
|
27386
|
-
"inputFields": null,
|
27387
|
-
"interfaces": [],
|
27388
|
-
"enumValues": null,
|
27389
|
-
"possibleTypes": null
|
27390
|
-
},
|
27391
27453
|
{
|
27392
27454
|
"kind": "OBJECT",
|
27393
27455
|
"name": "AWSVPC_v1",
|
reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py
CHANGED
@@ -41,7 +41,10 @@ query JiraBoardsForPermissionValidation {
|
|
41
41
|
issueType
|
42
42
|
issueResolveState
|
43
43
|
issueReopenState
|
44
|
-
|
44
|
+
issueFields {
|
45
|
+
name
|
46
|
+
value
|
47
|
+
}
|
45
48
|
severityPriorityMappings {
|
46
49
|
name
|
47
50
|
mappings {
|
@@ -73,6 +76,11 @@ class JiraServerV1(ConfiguredBaseModel):
|
|
73
76
|
token: VaultSecret = Field(..., alias="token")
|
74
77
|
|
75
78
|
|
79
|
+
class JiraBoardIssueFieldV1(ConfiguredBaseModel):
|
80
|
+
name: str = Field(..., alias="name")
|
81
|
+
value: str = Field(..., alias="value")
|
82
|
+
|
83
|
+
|
76
84
|
class SeverityPriorityMappingV1(ConfiguredBaseModel):
|
77
85
|
priority: str = Field(..., alias="priority")
|
78
86
|
|
@@ -102,7 +110,7 @@ class JiraBoardV1(ConfiguredBaseModel):
|
|
102
110
|
issue_type: Optional[str] = Field(..., alias="issueType")
|
103
111
|
issue_resolve_state: Optional[str] = Field(..., alias="issueResolveState")
|
104
112
|
issue_reopen_state: Optional[str] = Field(..., alias="issueReopenState")
|
105
|
-
|
113
|
+
issue_fields: Optional[list[JiraBoardIssueFieldV1]] = Field(..., alias="issueFields")
|
106
114
|
severity_priority_mappings: JiraSeverityPriorityMappingsV1 = Field(..., alias="severityPriorityMappings")
|
107
115
|
escalation_policies: Optional[list[AppEscalationPolicyV1]] = Field(..., alias="escalationPolicies")
|
108
116
|
disable: Optional[DisableJiraBoardAutomationsV1] = Field(..., alias="disable")
|
@@ -56,7 +56,7 @@ class ValidationError(IntFlag):
|
|
56
56
|
CANT_TRANSITION_ISSUES = auto()
|
57
57
|
INVALID_ISSUE_TYPE = auto()
|
58
58
|
INVALID_ISSUE_STATE = auto()
|
59
|
-
|
59
|
+
INVALID_ISSUE_FIELD = auto()
|
60
60
|
INVALID_PRIORITY = auto()
|
61
61
|
PERMISSION_ERROR = auto()
|
62
62
|
PUBLIC_PROJECT_NO_SECURITY_LEVEL = auto()
|
@@ -64,6 +64,9 @@ class ValidationError(IntFlag):
|
|
64
64
|
PROJECT_ARCHIVED = auto()
|
65
65
|
|
66
66
|
|
67
|
+
CustomIssueFields = dict[str, dict[str, str]]
|
68
|
+
|
69
|
+
|
67
70
|
class RunnerParams(TypedDict):
|
68
71
|
boards: list[JiraBoardV1]
|
69
72
|
board_check_interval_sec: int
|
@@ -81,12 +84,14 @@ def board_is_valid(
|
|
81
84
|
default_reopen_state: str,
|
82
85
|
jira_server_priorities: NameToIdMap,
|
83
86
|
public_projects: Iterable[str],
|
84
|
-
) -> ValidationError:
|
87
|
+
) -> tuple[ValidationError, CustomIssueFields]:
|
85
88
|
error = ValidationError(0)
|
89
|
+
custom_fields: CustomIssueFields = {}
|
90
|
+
|
86
91
|
try:
|
87
92
|
if jira.is_archived:
|
88
93
|
logging.error(f"[{board.name}] project is archived")
|
89
|
-
return ValidationError.PROJECT_ARCHIVED
|
94
|
+
return ValidationError.PROJECT_ARCHIVED, custom_fields
|
90
95
|
|
91
96
|
if not jira.can_create_issues():
|
92
97
|
logging.error(f"[{board.name}] can not create issues in project")
|
@@ -109,54 +114,71 @@ def board_is_valid(
|
|
109
114
|
error |= ValidationError.INVALID_COMPONENT
|
110
115
|
|
111
116
|
issue_type = board.issue_type or default_issue_type
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
f"[{board.name}] {issue_type} is not a valid issue type in project. Valid issue types: {project_issue_types_str}"
|
117
|
+
project_issue_type = jira.get_issue_type(issue_type)
|
118
|
+
if not project_issue_type:
|
119
|
+
project_issue_types_str = ", ".join(
|
120
|
+
t.name for t in jira.project_issue_types()
|
117
121
|
)
|
118
|
-
error |= ValidationError.INVALID_ISSUE_TYPE
|
119
|
-
|
120
|
-
available_states = []
|
121
|
-
for project_issue_type in project_issue_types:
|
122
|
-
if issue_type == project_issue_type.name:
|
123
|
-
available_states = project_issue_type.statuses
|
124
|
-
break
|
125
|
-
|
126
|
-
if not available_states:
|
127
122
|
logging.error(
|
128
|
-
f"[{board.name}] {issue_type}
|
123
|
+
f"[{board.name}] '{issue_type}' is not a valid issue type in project. Valid issue types: {project_issue_types_str}"
|
129
124
|
)
|
130
125
|
error |= ValidationError.INVALID_ISSUE_TYPE
|
131
126
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
127
|
+
if project_issue_type:
|
128
|
+
# Check issue attributes
|
129
|
+
if not project_issue_type.statuses:
|
130
|
+
logging.error(
|
131
|
+
f"[{board.name}] '{issue_type}' doesn't have any status. Choose a different issue type."
|
132
|
+
)
|
133
|
+
error |= ValidationError.INVALID_ISSUE_TYPE
|
138
134
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
135
|
+
reopen_state = board.issue_reopen_state or default_reopen_state
|
136
|
+
if reopen_state.lower() not in [
|
137
|
+
t.lower() for t in project_issue_type.statuses
|
138
|
+
]:
|
139
|
+
logging.error(
|
140
|
+
f"[{board.name}] '{reopen_state}' is not a valid state in project. Valid states: {project_issue_type.statuses}"
|
141
|
+
)
|
142
|
+
error |= ValidationError.INVALID_ISSUE_STATE
|
146
143
|
|
147
|
-
|
148
|
-
|
149
|
-
|
144
|
+
if board.issue_resolve_state and board.issue_resolve_state.lower() not in [
|
145
|
+
t.lower() for t in project_issue_type.statuses
|
146
|
+
]:
|
150
147
|
logging.error(
|
151
|
-
f"[{board.name}] {board.
|
152
|
-
+ ", ".join([
|
153
|
-
f"{level.name} - {level.id}" for level in jira.security_levels()
|
154
|
-
])
|
148
|
+
f"[{board.name}] '{board.issue_resolve_state}' is not a valid state in project. Valid states: {project_issue_type.statuses}"
|
155
149
|
)
|
156
|
-
error |= ValidationError.
|
157
|
-
|
150
|
+
error |= ValidationError.INVALID_ISSUE_STATE
|
151
|
+
|
152
|
+
for field in board.issue_fields or []:
|
153
|
+
project_issue_field = jira.project_issue_field(
|
154
|
+
issue_type_id=project_issue_type.id, field=field.name
|
155
|
+
)
|
156
|
+
if not project_issue_field:
|
157
|
+
logging.error(
|
158
|
+
f"[{board.name}] '{field.name}' is not a valid field for '{project_issue_type.name}' in this project."
|
159
|
+
)
|
160
|
+
|
161
|
+
error |= ValidationError.INVALID_ISSUE_FIELD
|
162
|
+
continue
|
163
|
+
|
164
|
+
for option in project_issue_field.options:
|
165
|
+
if option == field.value:
|
166
|
+
# cache the field id and option value/name for later use, e.g. in jiralert templates
|
167
|
+
custom_fields[project_issue_field.id] = option.dict()
|
168
|
+
break
|
169
|
+
else:
|
170
|
+
# field.value is not in the allowed options
|
171
|
+
logging.error(
|
172
|
+
f"[{board.name}] '{field.name}' has an invalid value '{field.value}'. Valid values: {project_issue_field.options}"
|
173
|
+
)
|
174
|
+
error |= ValidationError.INVALID_ISSUE_FIELD
|
175
|
+
continue
|
176
|
+
|
177
|
+
if board.name in public_projects and "Security Level" not in [
|
178
|
+
f.name for f in board.issue_fields or []
|
179
|
+
]:
|
158
180
|
logging.error(
|
159
|
-
f"[{board.name}] is a public project, but
|
181
|
+
f"[{board.name}] is a public project, but the security level is not defined. Please add 'Security Level' field to the issueFields!"
|
160
182
|
)
|
161
183
|
error |= ValidationError.PUBLIC_PROJECT_NO_SECURITY_LEVEL
|
162
184
|
|
@@ -192,7 +214,7 @@ def board_is_valid(
|
|
192
214
|
else:
|
193
215
|
raise
|
194
216
|
|
195
|
-
return error
|
217
|
+
return error, custom_fields
|
196
218
|
|
197
219
|
|
198
220
|
def validate_boards(
|
@@ -231,7 +253,7 @@ def validate_boards(
|
|
231
253
|
jira = jira_clients[board.server.server_url]
|
232
254
|
jira.project = board.name
|
233
255
|
try:
|
234
|
-
error_flags = board_is_valid(
|
256
|
+
error_flags, custom_fields = board_is_valid(
|
235
257
|
jira=jira,
|
236
258
|
board=board,
|
237
259
|
default_issue_type=default_issue_type,
|
@@ -271,6 +293,9 @@ def validate_boards(
|
|
271
293
|
error = True
|
272
294
|
case _:
|
273
295
|
error = True
|
296
|
+
|
297
|
+
# cache the field ids for later use, e.g. in jiralert templates
|
298
|
+
state[f"{board.name}-ids"] = custom_fields
|
274
299
|
except Exception as e:
|
275
300
|
logging.error(f"[{board.name}] {e}")
|
276
301
|
error = True
|
reconcile/utils/acs/notifiers.py
CHANGED
@@ -82,8 +82,10 @@ class JiraNotifier(BaseModel):
|
|
82
82
|
jira_board = escalation_policy.channels.jira_board[0]
|
83
83
|
|
84
84
|
custom_fields: dict[str, Any] = {}
|
85
|
-
|
86
|
-
|
85
|
+
for field in jira_board.issue_fields or []:
|
86
|
+
if field.name == "Security Level":
|
87
|
+
custom_fields["security"] = {"name": field.value}
|
88
|
+
|
87
89
|
if escalation_policy.channels.jira_component:
|
88
90
|
custom_fields["components"] = [
|
89
91
|
{"name": escalation_policy.channels.jira_component}
|
reconcile/utils/jira_client.py
CHANGED
@@ -16,6 +16,8 @@ from jira import (
|
|
16
16
|
Issue,
|
17
17
|
)
|
18
18
|
from jira.client import ResultList
|
19
|
+
from jira.resources import CustomFieldOption as JiraCustomFieldOption
|
20
|
+
from jira.resources import Resource
|
19
21
|
from pydantic import BaseModel
|
20
22
|
|
21
23
|
from reconcile.utils.secret_reader import SecretReader
|
@@ -26,13 +28,6 @@ class JiraWatcherSettings(Protocol):
|
|
26
28
|
connect_timeout: int
|
27
29
|
|
28
30
|
|
29
|
-
class SecurityLevel(BaseModel):
|
30
|
-
"""Jira security level."""
|
31
|
-
|
32
|
-
id: str
|
33
|
-
name: str
|
34
|
-
|
35
|
-
|
36
31
|
class Priority(BaseModel):
|
37
32
|
"""Jira priority."""
|
38
33
|
|
@@ -48,6 +43,50 @@ class IssueType(BaseModel):
|
|
48
43
|
statuses: list[str]
|
49
44
|
|
50
45
|
|
46
|
+
class FieldOption(BaseModel):
|
47
|
+
"""A standard buildin issue field option."""
|
48
|
+
|
49
|
+
name: str
|
50
|
+
|
51
|
+
def __eq__(self, value: Any) -> bool:
|
52
|
+
"""Compare the field option with a string value."""
|
53
|
+
if isinstance(value, str):
|
54
|
+
return self.name == value
|
55
|
+
return False
|
56
|
+
|
57
|
+
def __str__(self) -> str:
|
58
|
+
return self.name
|
59
|
+
|
60
|
+
def __repr__(self) -> str:
|
61
|
+
return self.__str__()
|
62
|
+
|
63
|
+
|
64
|
+
class CustomFieldOption(BaseModel):
|
65
|
+
"""A custom issue field option."""
|
66
|
+
|
67
|
+
value: str
|
68
|
+
|
69
|
+
def __eq__(self, value: Any) -> bool:
|
70
|
+
"""Compare the custom field option with a string value."""
|
71
|
+
if isinstance(value, str):
|
72
|
+
return self.value == value
|
73
|
+
return False
|
74
|
+
|
75
|
+
def __str__(self) -> str:
|
76
|
+
return self.value
|
77
|
+
|
78
|
+
def __repr__(self) -> str:
|
79
|
+
return self.__str__()
|
80
|
+
|
81
|
+
|
82
|
+
class IssueField(BaseModel):
|
83
|
+
"""Jira issue field."""
|
84
|
+
|
85
|
+
id: str
|
86
|
+
name: str
|
87
|
+
options: list[FieldOption | CustomFieldOption]
|
88
|
+
|
89
|
+
|
51
90
|
class JiraClient:
|
52
91
|
"""Wrapper around Jira client."""
|
53
92
|
|
@@ -86,6 +125,8 @@ class JiraClient:
|
|
86
125
|
self.priorities = functools.lru_cache(maxsize=None)(self._priorities)
|
87
126
|
self.public_projects = functools.lru_cache(maxsize=None)(self._public_projects)
|
88
127
|
self.my_permissions = functools.lru_cache(maxsize=None)(self._my_permissions)
|
128
|
+
self.project_issue_types = functools.cache(self._project_issue_types)
|
129
|
+
self.project_issue_fields = functools.cache(self._project_issue_fields)
|
89
130
|
|
90
131
|
def _deprecated_init(
|
91
132
|
self, jira_board: Mapping[str, Any], settings: Mapping | None
|
@@ -199,19 +240,63 @@ class JiraClient:
|
|
199
240
|
def can_transition_issues(self) -> bool:
|
200
241
|
return self.can_i("TRANSITION_ISSUES")
|
201
242
|
|
202
|
-
def
|
243
|
+
def _project_issue_types(self, project: str) -> list[IssueType]:
|
244
|
+
# Don't use self.project here, because of function.cache usage
|
203
245
|
return [
|
204
246
|
IssueType(id=t.id, name=t.name, statuses=[s.name for s in t.statuses])
|
205
|
-
for t in self.jira.issue_types_for_project(
|
247
|
+
for t in self.jira.issue_types_for_project(project)
|
248
|
+
]
|
249
|
+
|
250
|
+
def get_issue_type(self, issue_type: str) -> IssueType | None:
|
251
|
+
for _issue_type in self.project_issue_types(self.project):
|
252
|
+
if _issue_type.name == issue_type:
|
253
|
+
return _issue_type
|
254
|
+
return None
|
255
|
+
|
256
|
+
@staticmethod
|
257
|
+
def _get_allowed_issue_field_options(
|
258
|
+
allowed_values: list[Resource],
|
259
|
+
) -> list[FieldOption | CustomFieldOption]:
|
260
|
+
"""Return a list of allowed values for a field."""
|
261
|
+
return [
|
262
|
+
CustomFieldOption(value=v.value)
|
263
|
+
if isinstance(v, JiraCustomFieldOption)
|
264
|
+
else FieldOption(name=v.name)
|
265
|
+
for v in allowed_values
|
266
|
+
]
|
267
|
+
|
268
|
+
def _project_issue_fields(
|
269
|
+
self, project: str, issue_type_id: str
|
270
|
+
) -> list[IssueField]:
|
271
|
+
"""Return all available issue fields for the project.
|
272
|
+
|
273
|
+
This API endpoint needs createIssue project permissions.
|
274
|
+
"""
|
275
|
+
# Don't use self.project here, because of function.cache usage
|
276
|
+
return [
|
277
|
+
IssueField(
|
278
|
+
name=field.name,
|
279
|
+
id=field.fieldId,
|
280
|
+
options=self._get_allowed_issue_field_options(
|
281
|
+
getattr(field, "allowedValues", [])
|
282
|
+
),
|
283
|
+
)
|
284
|
+
for field in self.jira.project_issue_fields(
|
285
|
+
project=project, issue_type=issue_type_id, maxResults=9999
|
286
|
+
)
|
206
287
|
]
|
207
288
|
|
208
|
-
def
|
209
|
-
"""Return a
|
289
|
+
def project_issue_field(self, issue_type_id: str, field: str) -> IssueField | None:
|
290
|
+
"""Return a issue field for the project if it exists.
|
210
291
|
|
211
|
-
This API endpoint needs
|
292
|
+
This API endpoint needs createIssue project permissions.
|
212
293
|
"""
|
213
|
-
|
214
|
-
|
294
|
+
for _field in self.project_issue_fields(
|
295
|
+
project=self.project, issue_type_id=issue_type_id
|
296
|
+
):
|
297
|
+
if _field.name == field:
|
298
|
+
return _field
|
299
|
+
return None
|
215
300
|
|
216
301
|
def _priorities(self) -> list[Priority]:
|
217
302
|
"""Return a list of all available Jira priorities."""
|
{qontract_reconcile-0.10.2.dev178.dist-info → qontract_reconcile-0.10.2.dev180.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|