qontract-reconcile 0.9.1rc162__py3-none-any.whl → 0.9.1rc164__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.
Files changed (30) hide show
  1. {qontract_reconcile-0.9.1rc162.dist-info → qontract_reconcile-0.9.1rc164.dist-info}/METADATA +2 -2
  2. {qontract_reconcile-0.9.1rc162.dist-info → qontract_reconcile-0.9.1rc164.dist-info}/RECORD +21 -30
  3. reconcile/glitchtip_project_dsn/integration.py +3 -0
  4. reconcile/jenkins_job_builder.py +2 -5
  5. reconcile/openshift_base.py +11 -11
  6. reconcile/openshift_saas_deploy.py +52 -57
  7. reconcile/openshift_saas_deploy_trigger_base.py +48 -55
  8. reconcile/openshift_saas_deploy_trigger_cleaner.py +2 -2
  9. reconcile/openshift_tekton_resources.py +1 -1
  10. reconcile/saas_file_validator.py +10 -23
  11. reconcile/slack_base.py +2 -5
  12. reconcile/test/conftest.py +0 -11
  13. reconcile/test/test_auto_promoter.py +42 -199
  14. reconcile/test/test_saasherder.py +463 -398
  15. reconcile/test/test_saasherder_allowed_secret_paths.py +36 -87
  16. reconcile/utils/mr/auto_promoter.py +50 -58
  17. reconcile/utils/mr/base.py +2 -6
  18. reconcile/utils/{saasherder/saasherder.py → saasherder.py} +736 -656
  19. reconcile/gql_definitions/common/app_code_component_repos.py +0 -68
  20. reconcile/gql_definitions/common/saas_files.py +0 -542
  21. reconcile/gql_definitions/common/saasherder_settings.py +0 -62
  22. reconcile/gql_definitions/fragments/oc_connection_cluster.py +0 -47
  23. reconcile/typed_queries/repos.py +0 -17
  24. reconcile/typed_queries/saas_files.py +0 -61
  25. reconcile/utils/saasherder/__init__.py +0 -17
  26. reconcile/utils/saasherder/interfaces.py +0 -404
  27. reconcile/utils/saasherder/models.py +0 -203
  28. {qontract_reconcile-0.9.1rc162.dist-info → qontract_reconcile-0.9.1rc164.dist-info}/WHEEL +0 -0
  29. {qontract_reconcile-0.9.1rc162.dist-info → qontract_reconcile-0.9.1rc164.dist-info}/entry_points.txt +0 -0
  30. {qontract_reconcile-0.9.1rc162.dist-info → qontract_reconcile-0.9.1rc164.dist-info}/top_level.txt +0 -0
@@ -1,17 +0,0 @@
1
- from collections.abc import Callable
2
- from typing import Optional
3
-
4
- from reconcile.gql_definitions.common.app_code_component_repos import query
5
- from reconcile.utils import gql
6
-
7
-
8
- def get_repos(
9
- server: str = "",
10
- query_func: Optional[Callable] = None,
11
- ) -> list[str]:
12
- if not query_func:
13
- query_func = gql.get_api().query
14
- repos: list[str] = []
15
- for app in query(query_func).apps or []:
16
- repos += [c.url for c in app.code_components or [] if c.url.startswith(server)]
17
- return repos
@@ -1,61 +0,0 @@
1
- from collections.abc import Callable
2
- from typing import Optional
3
-
4
- from reconcile.gql_definitions.common.saas_files import SaasFileV2
5
- from reconcile.gql_definitions.common.saas_files import query as saas_files_query
6
- from reconcile.gql_definitions.common.saasherder_settings import AppInterfaceSettingsV1
7
- from reconcile.gql_definitions.common.saasherder_settings import (
8
- query as saasherder_settings_query,
9
- )
10
- from reconcile.utils import gql
11
- from reconcile.utils.exceptions import AppInterfaceSettingsError
12
-
13
-
14
- def get_saas_files(
15
- name: Optional[str] = None,
16
- env_name: Optional[str] = None,
17
- app_name: Optional[str] = None,
18
- query_func: Optional[Callable] = None,
19
- ) -> list[SaasFileV2]:
20
- if not query_func:
21
- query_func = gql.get_api().query
22
- data = saas_files_query(query_func)
23
- saas_files = list(data.saas_files or [])
24
- if name is None and env_name is None and app_name is None:
25
- return saas_files
26
- if name == "" or env_name == "" or app_name == "":
27
- return []
28
-
29
- for saas_file in saas_files[:]:
30
- if name:
31
- if saas_file.name != name:
32
- saas_files.remove(saas_file)
33
- continue
34
-
35
- if env_name:
36
- for rt in saas_file.resource_templates[:]:
37
- for target in rt.targets[:]:
38
- if target.namespace.environment.name != env_name:
39
- rt.targets.remove(target)
40
- if not rt.targets:
41
- saas_file.resource_templates.remove(rt)
42
- if not saas_file.resource_templates:
43
- saas_files.remove(saas_file)
44
- continue
45
-
46
- if app_name:
47
- if saas_file.app.name != app_name:
48
- saas_files.remove(saas_file)
49
- continue
50
-
51
- return saas_files
52
-
53
-
54
- def get_saasherder_settings(
55
- query_func: Optional[Callable] = None,
56
- ) -> AppInterfaceSettingsV1:
57
- if not query_func:
58
- query_func = gql.get_api().query
59
- if _settings := saasherder_settings_query(query_func).settings:
60
- return _settings[0]
61
- raise AppInterfaceSettingsError("settings missing")
@@ -1,17 +0,0 @@
1
- from reconcile.utils.saasherder.models import (
2
- Providers,
3
- TriggerSpecUnion,
4
- TriggerTypes,
5
- )
6
- from reconcile.utils.saasherder.saasherder import (
7
- UNIQUE_SAAS_FILE_ENV_COMBO_LEN,
8
- SaasHerder,
9
- )
10
-
11
- __all__ = [
12
- "Providers",
13
- "SaasHerder",
14
- "TriggerSpecUnion",
15
- "TriggerTypes",
16
- "UNIQUE_SAAS_FILE_ENV_COMBO_LEN",
17
- ]
@@ -1,404 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from collections.abc import Sequence
4
- from typing import (
5
- AbstractSet,
6
- Any,
7
- Mapping,
8
- Optional,
9
- Protocol,
10
- Union,
11
- runtime_checkable,
12
- )
13
-
14
- from reconcile.utils import oc_connection_parameters
15
- from reconcile.utils.secret_reader import HasSecret
16
-
17
-
18
- class HasParameters(Protocol):
19
- parameters: Optional[dict[str, Any]]
20
-
21
-
22
- class SaasFileSecretParameters(Protocol):
23
- name: str
24
-
25
- @property
26
- def secret(self) -> HasSecret:
27
- ...
28
-
29
-
30
- SaasSecretParameters = Optional[Sequence[SaasFileSecretParameters]]
31
- # Taken from pydantic.typing
32
- AbstractSetIntStr = AbstractSet[Union[int, str]]
33
- MappingIntStrAny = Mapping[Union[int, str], Any]
34
-
35
-
36
- class HasSecretParameters(Protocol):
37
- @property
38
- def secret_parameters(self) -> SaasSecretParameters:
39
- ...
40
-
41
-
42
- class SaasApp(Protocol):
43
- name: str
44
-
45
-
46
- class SaasPipelinesProvider(Protocol):
47
- name: str
48
- provider: str
49
-
50
-
51
- class SaasPipelinesProviderTektonNamespaceCluster(
52
- oc_connection_parameters.Cluster, Protocol
53
- ):
54
- console_url: str
55
-
56
-
57
- class SaasPipelinesProviderTektonNamespace(Protocol):
58
- name: str
59
-
60
- @property
61
- def cluster(self) -> SaasPipelinesProviderTektonNamespaceCluster:
62
- ...
63
-
64
-
65
- class SaasPipelinesProviderTektonObjectTemplate(Protocol):
66
- name: str
67
-
68
-
69
- class SaasPipelinesProviderPipelineTemplates(Protocol):
70
- @property
71
- def openshift_saas_deploy(self) -> SaasPipelinesProviderTektonObjectTemplate:
72
- ...
73
-
74
-
75
- class SaasPipelinesProviderTektonProviderDefaults(Protocol):
76
- @property
77
- def pipeline_templates(self) -> SaasPipelinesProviderPipelineTemplates:
78
- ...
79
-
80
-
81
- class SaasPipelinesProviderTekton_PipelinesProviderPipelineTemplates_PipelinesProviderTektonObjectTemplate(
82
- Protocol
83
- ):
84
- name: str
85
-
86
-
87
- class SaasPipelinesProviderTekton_PipelinesProviderPipelineTemplates(Protocol):
88
- @property
89
- def openshift_saas_deploy(
90
- self,
91
- ) -> SaasPipelinesProviderTekton_PipelinesProviderPipelineTemplates_PipelinesProviderTektonObjectTemplate:
92
- ...
93
-
94
-
95
- @runtime_checkable
96
- class SaasPipelinesProviderTekton(Protocol):
97
- name: str
98
- provider: str
99
-
100
- @property
101
- def namespace(self) -> SaasPipelinesProviderTektonNamespace:
102
- ...
103
-
104
- @property
105
- def defaults(self) -> SaasPipelinesProviderTektonProviderDefaults:
106
- ...
107
-
108
- @property
109
- def pipeline_templates(
110
- self,
111
- ) -> Optional[SaasPipelinesProviderTekton_PipelinesProviderPipelineTemplates]:
112
- ...
113
-
114
-
115
- class SaasResourceRequirements(Protocol):
116
- cpu: str
117
- memory: str
118
-
119
-
120
- class SaasDeployResources(Protocol):
121
- @property
122
- def requests(self) -> SaasResourceRequirements:
123
- ...
124
-
125
- @property
126
- def limits(self) -> SaasResourceRequirements:
127
- ...
128
-
129
-
130
- class SaasSlackWorkspaceIntegration(Protocol):
131
- name: str
132
- channel: str
133
- icon_emoji: str
134
- username: str
135
-
136
- @property
137
- def token(self) -> HasSecret:
138
- ...
139
-
140
-
141
- class SaasSlackWorkspace(Protocol):
142
- name: str
143
-
144
- @property
145
- def integrations(self) -> Optional[Sequence[SaasSlackWorkspaceIntegration]]:
146
- ...
147
-
148
-
149
- class SaasSlackOutputNotifications(Protocol):
150
- start: Optional[bool]
151
-
152
-
153
- class SaasSlackOutput(Protocol):
154
- output: Optional[str]
155
- channel: Optional[str]
156
-
157
- @property
158
- def workspace(self) -> SaasSlackWorkspace:
159
- ...
160
-
161
- @property
162
- def notifications(self) -> Optional[SaasSlackOutputNotifications]:
163
- ...
164
-
165
-
166
- class SaasFileAuthentication(Protocol):
167
- @property
168
- def code(self) -> Optional[HasSecret]:
169
- ...
170
-
171
- @property
172
- def image(self) -> Optional[HasSecret]:
173
- ...
174
-
175
-
176
- class SaasEnvironment_SaasSecretParameters(Protocol):
177
- name: str
178
-
179
- @property
180
- def secret(self) -> HasSecret:
181
- ...
182
-
183
-
184
- class SaasEnvironment(HasParameters, HasSecretParameters, Protocol):
185
- name: str
186
-
187
-
188
- class SaasResourceTemplateTargetNamespace(Protocol):
189
- name: str
190
-
191
- @property
192
- def environment(self) -> SaasEnvironment:
193
- ...
194
-
195
- @property
196
- def app(self) -> SaasApp:
197
- ...
198
-
199
- @property
200
- def cluster(self) -> oc_connection_parameters.Cluster:
201
- ...
202
-
203
- def dict(
204
- self,
205
- *,
206
- by_alias: bool = False,
207
- include: Union[AbstractSetIntStr, MappingIntStrAny],
208
- ) -> dict[str, Any]:
209
- ...
210
-
211
-
212
- class SaasPromotionChannelData(Protocol):
213
- q_type: str
214
-
215
-
216
- @runtime_checkable
217
- class SaasParentSaasPromotion(Protocol):
218
- q_type: str
219
- parent_saas: Optional[str]
220
- target_config_hash: Optional[str]
221
-
222
-
223
- class SaasPromotionData(Protocol):
224
- channel: Optional[str]
225
-
226
- @property
227
- def data(
228
- self,
229
- ) -> Optional[Sequence[Union[SaasParentSaasPromotion, SaasPromotionChannelData]]]:
230
- ...
231
-
232
-
233
- class SaasResourceTemplateTargetPromotion(Protocol):
234
- auto: Optional[bool]
235
- publish: Optional[list[str]]
236
- subscribe: Optional[list[str]]
237
-
238
- @property
239
- def promotion_data(self) -> Optional[Sequence[SaasPromotionData]]:
240
- ...
241
-
242
-
243
- @runtime_checkable
244
- class SaasPromotion(Protocol):
245
- commit_sha: str
246
- saas_file_name: str
247
- target_config_hash: str
248
- auto: Optional[bool] = None
249
- publish: Optional[list[str]] = None
250
- subscribe: Optional[list[str]] = None
251
- saas_file_paths: Optional[list[str]] = None
252
- target_paths: Optional[list[str]] = None
253
-
254
- @property
255
- def promotion_data(self) -> Optional[Sequence[SaasPromotionData]]:
256
- ...
257
-
258
- def dict(self, *, by_alias: bool = False) -> dict[str, Any]:
259
- ...
260
-
261
-
262
- class SaasResourceTemplateTarget_SaasSecretParameters(Protocol):
263
- name: str
264
-
265
- @property
266
- def secret(self) -> HasSecret:
267
- ...
268
-
269
-
270
- class SaasJenkinsInstance(Protocol):
271
- name: str
272
- server_url: str
273
-
274
-
275
- class SaasResourceTemplateTargetUpstream(Protocol):
276
- name: str
277
-
278
- @property
279
- def instance(self) -> SaasJenkinsInstance:
280
- ...
281
-
282
-
283
- class SaasQuayInstance(Protocol):
284
- url: str
285
-
286
-
287
- class SaasQuayOrg(Protocol):
288
- name: str
289
-
290
- @property
291
- def instance(self) -> SaasQuayInstance:
292
- ...
293
-
294
-
295
- class SaasResourceTemplateTargetImage(Protocol):
296
- name: str
297
-
298
- @property
299
- def org(self) -> SaasQuayOrg:
300
- ...
301
-
302
-
303
- class SaasResourceTemplateTarget(HasParameters, HasSecretParameters, Protocol):
304
- path: Optional[str]
305
- name: Optional[str]
306
- disable: Optional[bool]
307
- delete: Optional[bool]
308
- ref: str
309
-
310
- @property
311
- def namespace(self) -> SaasResourceTemplateTargetNamespace:
312
- ...
313
-
314
- @property
315
- def promotion(self) -> Optional[SaasResourceTemplateTargetPromotion]:
316
- ...
317
-
318
- @property
319
- def upstream(self) -> Optional[SaasResourceTemplateTargetUpstream]:
320
- ...
321
-
322
- @property
323
- def image(self) -> Optional[SaasResourceTemplateTargetImage]:
324
- ...
325
-
326
- def dict(self, *, by_alias: bool = False) -> dict[str, Any]:
327
- ...
328
-
329
-
330
- class SaasResourceTemplate(HasParameters, HasSecretParameters, Protocol):
331
- name: str
332
- url: str
333
- path: str
334
- provider: Optional[str]
335
- hash_length: Optional[int]
336
-
337
- @property
338
- def targets(self) -> Sequence[SaasResourceTemplateTarget]:
339
- ...
340
-
341
-
342
- class SaasUser(Protocol):
343
- org_username: str
344
- tag_on_merge_requests: Optional[bool]
345
-
346
-
347
- class SaasBot(Protocol):
348
- org_username: Optional[str]
349
-
350
-
351
- class SaasRole(Protocol):
352
- @property
353
- def users(self) -> Sequence[SaasUser]:
354
- ...
355
-
356
- @property
357
- def bots(self) -> Sequence[SaasBot]:
358
- ...
359
-
360
-
361
- SaasPipelinesProviders = Union[SaasPipelinesProviderTekton, SaasPipelinesProvider]
362
-
363
-
364
- class SaasFile(HasParameters, HasSecretParameters, Protocol):
365
- path: str
366
- name: str
367
- managed_resource_types: list[str]
368
- takeover: Optional[bool]
369
- deprecated: Optional[bool]
370
- compare: Optional[bool]
371
- timeout: Optional[str]
372
- publish_job_logs: Optional[bool]
373
- cluster_admin: Optional[bool]
374
- image_patterns: list[str]
375
- allowed_secret_parameter_paths: Optional[list[str]]
376
- use_channel_in_image_tag: Optional[bool]
377
-
378
- @property
379
- def app(self) -> SaasApp:
380
- ...
381
-
382
- @property
383
- def pipelines_provider(self) -> SaasPipelinesProviders:
384
- ...
385
-
386
- @property
387
- def deploy_resources(self) -> Optional[SaasDeployResources]:
388
- ...
389
-
390
- @property
391
- def slack(self) -> Optional[SaasSlackOutput]:
392
- ...
393
-
394
- @property
395
- def authentication(self) -> Optional[SaasFileAuthentication]:
396
- ...
397
-
398
- @property
399
- def resource_templates(self) -> Sequence[SaasResourceTemplate]:
400
- ...
401
-
402
- @property
403
- def self_service_roles(self) -> Optional[Sequence[SaasRole]]:
404
- ...
@@ -1,203 +0,0 @@
1
- from collections.abc import Iterable
2
- from dataclasses import dataclass
3
- from enum import Enum
4
- from typing import (
5
- Any,
6
- Optional,
7
- Union,
8
- )
9
-
10
- from github import Github
11
- from pydantic import (
12
- BaseModel,
13
- Field,
14
- )
15
-
16
- from reconcile.utils.oc_connection_parameters import Cluster
17
- from reconcile.utils.saasherder.interfaces import (
18
- SaasApp,
19
- SaasEnvironment,
20
- SaasPipelinesProviders,
21
- SaasResourceTemplateTarget,
22
- )
23
-
24
-
25
- class Providers(Enum):
26
- TEKTON = "tekton"
27
-
28
-
29
- class TriggerTypes:
30
- CONFIGS = 0
31
- MOVING_COMMITS = 1
32
- UPSTREAM_JOBS = 2
33
- CONTAINER_IMAGES = 3
34
-
35
-
36
- @dataclass
37
- class UpstreamJob:
38
- instance: str
39
- job: str
40
-
41
- def __str__(self) -> str:
42
- return f"{self.instance}/{self.job}"
43
-
44
- def __repr__(self) -> str:
45
- return self.__str__()
46
-
47
-
48
- @dataclass
49
- class TriggerSpecBase:
50
- saas_file_name: str
51
- env_name: str
52
- timeout: Optional[str]
53
- pipelines_provider: SaasPipelinesProviders
54
- resource_template_name: str
55
- cluster_name: str
56
- namespace_name: str
57
- state_content: Any
58
-
59
- @property
60
- def state_key(self) -> str:
61
- raise NotImplementedError("implement this function in inheriting classes")
62
-
63
-
64
- @dataclass
65
- class TriggerSpecConfig(TriggerSpecBase):
66
- target_name: Optional[str] = None
67
- reason: Optional[str] = None
68
-
69
- @property
70
- def state_key(self) -> str:
71
- key = (
72
- f"{self.saas_file_name}/{self.resource_template_name}/{self.cluster_name}/"
73
- f"{self.namespace_name}/{self.env_name}"
74
- )
75
- if self.target_name:
76
- key += f"/{self.target_name}"
77
- return key
78
-
79
-
80
- @dataclass
81
- class TriggerSpecMovingCommit(TriggerSpecBase):
82
- ref: str
83
- reason: Optional[str] = None
84
-
85
- @property
86
- def state_key(self) -> str:
87
- key = (
88
- f"{self.saas_file_name}/{self.resource_template_name}/{self.cluster_name}/"
89
- f"{self.namespace_name}/{self.env_name}/{self.ref}"
90
- )
91
- return key
92
-
93
-
94
- @dataclass
95
- class TriggerSpecUpstreamJob(TriggerSpecBase):
96
- instance_name: str
97
- job_name: str
98
- reason: Optional[str] = None
99
-
100
- @property
101
- def state_key(self) -> str:
102
- key = (
103
- f"{self.saas_file_name}/{self.resource_template_name}/{self.cluster_name}/"
104
- f"{self.namespace_name}/{self.env_name}/{self.instance_name}/{self.job_name}"
105
- )
106
- return key
107
-
108
-
109
- @dataclass
110
- class TriggerSpecContainerImage(TriggerSpecBase):
111
- image: str
112
- reason: Optional[str] = None
113
-
114
- @property
115
- def state_key(self) -> str:
116
- key = (
117
- f"{self.saas_file_name}/{self.resource_template_name}/{self.cluster_name}/"
118
- f"{self.namespace_name}/{self.env_name}/{self.image}"
119
- )
120
- return key
121
-
122
-
123
- TriggerSpecUnion = Union[
124
- TriggerSpecConfig,
125
- TriggerSpecMovingCommit,
126
- TriggerSpecUpstreamJob,
127
- TriggerSpecContainerImage,
128
- ]
129
-
130
-
131
- class Namespace(BaseModel):
132
- name: str
133
- environment: SaasEnvironment
134
- app: SaasApp
135
- cluster: Cluster
136
- managed_resource_types: list[str] = Field(..., alias="managedResourceTypes")
137
-
138
- class Config:
139
- arbitrary_types_allowed = True
140
- allow_population_by_field_name = True
141
-
142
-
143
- class PromotionChannelData(BaseModel):
144
- q_type: str = Field(..., alias="type")
145
-
146
- class Config:
147
- allow_population_by_field_name = True
148
-
149
-
150
- class ParentSaasPromotion(BaseModel):
151
- q_type: str = Field(..., alias="type")
152
- parent_saas: Optional[str]
153
- target_config_hash: Optional[str]
154
-
155
- class Config:
156
- allow_population_by_field_name = True
157
-
158
-
159
- class PromotionData(BaseModel):
160
- channel: Optional[str]
161
- data: Optional[list[Union[ParentSaasPromotion, PromotionChannelData]]] = None
162
-
163
-
164
- class Promotion(BaseModel):
165
- """Implementation of the SaasPromotion interface for saasherder and AutoPromoter."""
166
-
167
- commit_sha: str
168
- saas_file_name: str
169
- target_config_hash: str
170
- auto: Optional[bool] = None
171
- publish: Optional[list[str]] = None
172
- subscribe: Optional[list[str]] = None
173
- promotion_data: Optional[list[PromotionData]] = None
174
- saas_file_paths: Optional[list[str]] = None
175
- target_paths: Optional[list[str]] = None
176
-
177
-
178
- @dataclass
179
- class ImageAuth:
180
- username: Optional[str] = None
181
- password: Optional[str] = None
182
- auth_server: Optional[str] = None
183
-
184
-
185
- @dataclass
186
- class TargetSpec:
187
- saas_file_name: str
188
- resource_template_name: str
189
- target: SaasResourceTemplateTarget
190
- cluster: str
191
- namespace: str
192
- managed_resource_types: Iterable[str]
193
- delete: bool
194
- privileged: bool
195
- image_auth: ImageAuth
196
- image_patterns: list[str]
197
- url: str
198
- path: str
199
- provider: str
200
- hash_length: int
201
- parameters: dict[str, str]
202
- github: Github
203
- target_config_hash: str