qontract-reconcile 0.10.2.dev177__py3-none-any.whl → 0.10.2.dev179__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.4
2
2
  Name: qontract-reconcile
3
- Version: 0.10.2.dev177
3
+ Version: 0.10.2.dev179
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
@@ -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=Sw7l-EMHwPk5HXA0LqOs2vmvDd-zraVHouUX1Ak2cVM,13524
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
@@ -141,7 +141,7 @@ reconcile/aus/version_gates/ocp_gate_handler.py,sha256=RW1ppDaCZXVegV9AzzqYXxDUu
141
141
  reconcile/aus/version_gates/sts_version_gate_handler.py,sha256=swwwz0YyvrEBf_InqrRRBCt2QzHYNvvq8jz9aYwElh4,3663
142
142
  reconcile/automated_actions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
143
143
  reconcile/automated_actions/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
144
- reconcile/automated_actions/config/integration.py,sha256=cf1VxolcjlGP-xO7IIH5A00Qr4znNJTT7a_sU18ABig,10755
144
+ reconcile/automated_actions/config/integration.py,sha256=iOzR8PyWwKmUoB6Zx4bmbWFEiDPsP9tXuWia_bSW4vA,10921
145
145
  reconcile/aws_account_manager/README.md,sha256=_XFM3GZNHUzv--e_navqJuaUWpjC6QrHfulreHynFf0,262
146
146
  reconcile/aws_account_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
147
147
  reconcile/aws_account_manager/integration.py,sha256=XTamC824imAezzVoQhhwdMOawNcPCOghR_y7i_8bpJI,15343
@@ -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=zD5QWpv9KYduZHr8a6dFoGz-oSHM1ItN1Trn02Lh7XQ,2297479
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=bN5i4mks10Z23KJSj7jqp966Osq2dps4d-sPH9gjxEA,7008
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=7p-GA-dGeuouUcAvOIMBbgGJJtIXPG5Jx4n024to97M,3856
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=oWi7rcAP1C59oIBTPg6kRntI25Zm4e7FyvdVYvZ9RZ8,7881
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=nHVw9C_2-K4nv5zq26jlOTSw1roY6TKlovi1sOfpxpI,4998
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.dev177.dist-info/METADATA,sha256=0-nd-z8GuU_IUpjauhA3gjoMnQSYcxgWNmMJNh1lgSE,24627
811
- qontract_reconcile-0.10.2.dev177.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
812
- qontract_reconcile-0.10.2.dev177.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
813
- qontract_reconcile-0.10.2.dev177.dist-info/RECORD,,
810
+ qontract_reconcile-0.10.2.dev179.dist-info/METADATA,sha256=vGUEKBGnRX-V-Y5trKn107wjTWzK8k9k_cbQ92eYeLE,24627
811
+ qontract_reconcile-0.10.2.dev179.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
812
+ qontract_reconcile-0.10.2.dev179.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
813
+ qontract_reconcile-0.10.2.dev179.dist-info/RECORD,,
@@ -53,6 +53,7 @@ class AutomatedActionsUser(BaseModel):
53
53
  class AutomatedActionsPolicy(BaseModel):
54
54
  sub: str
55
55
  obj: str
56
+ max_ops: int
56
57
  params: dict[str, str] = {}
57
58
 
58
59
 
@@ -142,7 +143,7 @@ class AutomatedActionsConfigIntegration(
142
143
  roles: AutomatedActionRoles = {}
143
144
 
144
145
  for permission in permissions or []:
145
- obj = permission.action.operation_id
146
+ action = permission.action
146
147
 
147
148
  parameters: list[dict[str, str]] = [] if permission.arguments else [{}]
148
149
  for arg in permission.arguments or []:
@@ -163,7 +164,12 @@ class AutomatedActionsConfigIntegration(
163
164
  for role in permission.roles or []:
164
165
  aa_role = roles.setdefault(role.name, [])
165
166
  aa_role.extend(
166
- AutomatedActionsPolicy(sub=role.name, obj=obj, params=params)
167
+ AutomatedActionsPolicy(
168
+ sub=role.name,
169
+ obj=action.operation_id,
170
+ max_ops=action.max_ops,
171
+ params=params,
172
+ )
167
173
  for params in parameters
168
174
  )
169
175
  return roles
@@ -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
- name
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
- issueSecurityId
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
- clusters {
65
+ clusters {
63
66
  name
64
- }
67
+ }
65
68
  }
66
69
  ... on AcsPolicyScopeNamespace_v1 {
67
- namespaces {
70
+ namespaces {
68
71
  name
69
72
  cluster {
70
73
  name
@@ -72,7 +75,7 @@ query AcsPolicy {
72
75
  }
73
76
  }
74
77
  }
75
- conditions {
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
- issue_security_id: Optional[str] = Field(..., alias="issueSecurityId")
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": "issueSecurityId",
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": "wontFixResolution",
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": "reopenDuration",
19402
+ "name": "disable",
19253
19403
  "description": null,
19254
19404
  "args": [],
19255
19405
  "type": {
19256
- "kind": "SCALAR",
19257
- "name": "String",
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": "disable",
19414
+ "name": "issueFields",
19265
19415
  "description": null,
19266
19416
  "args": [],
19267
19417
  "type": {
19268
- "kind": "OBJECT",
19269
- "name": "DisableJiraBoardAutomations_v1",
19270
- "ofType": null
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",
@@ -41,7 +41,10 @@ query JiraBoardsForPermissionValidation {
41
41
  issueType
42
42
  issueResolveState
43
43
  issueReopenState
44
- issueSecurityId
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
- issue_security_id: Optional[str] = Field(..., alias="issueSecurityId")
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
- INVALID_SECURITY_LEVEL = auto()
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
- project_issue_types = jira.project_issue_types()
113
- project_issue_types_str = [i.name for i in project_issue_types]
114
- if issue_type not in project_issue_types_str:
115
- logging.error(
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} doesn't have any status. Choose a different 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
- reopen_state = board.issue_reopen_state or default_reopen_state
133
- if reopen_state.lower() not in [t.lower() for t in available_states]:
134
- logging.error(
135
- f"[{board.name}] '{reopen_state}' is not a valid state in project. Valid states: {available_states}"
136
- )
137
- error |= ValidationError.INVALID_ISSUE_STATE
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
- if board.issue_resolve_state and board.issue_resolve_state.lower() not in [
140
- t.lower() for t in available_states
141
- ]:
142
- logging.error(
143
- f"[{board.name}] '{board.issue_resolve_state}' is not a valid state in project. Valid states: {available_states}"
144
- )
145
- error |= ValidationError.INVALID_ISSUE_STATE
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
- if board.issue_security_id:
148
- security_levels = jira.security_levels()
149
- if board.issue_security_id not in [level.id for level in security_levels]:
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.issue_security_id} is not a valid security level in project. Valid security ids: "
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.INVALID_SECURITY_LEVEL
157
- elif board.name in public_projects:
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 no security level is defined."
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
@@ -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
- if jira_board.issue_security_id:
86
- custom_fields["security"] = {"id": jira_board.issue_security_id}
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}
@@ -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 project_issue_types(self) -> list[IssueType]:
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(self.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 security_levels(self) -> list[SecurityLevel]:
209
- """Return a list of all available security levels for the project.
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 admin/owner project permissions.
292
+ This API endpoint needs createIssue project permissions.
212
293
  """
213
- scheme = self.jira.project_issue_security_level_scheme(self.project)
214
- return [SecurityLevel(id=level.id, name=level.name) for level in scheme.levels]
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."""