qontract-reconcile 0.10.1rc812__py3-none-any.whl → 0.10.1rc814__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.1rc812.dist-info → qontract_reconcile-0.10.1rc814.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.1rc812.dist-info → qontract_reconcile-0.10.1rc814.dist-info}/RECORD +11 -11
- reconcile/gql_definitions/maintenance/maintenances.py +6 -0
- reconcile/gql_definitions/statuspage/statuspages.py +14 -0
- reconcile/statuspage/atlassian.py +69 -23
- reconcile/statuspage/integrations/maintenances.py +12 -2
- reconcile/statuspage/page.py +56 -4
- tools/qontract_cli.py +1 -1
- {qontract_reconcile-0.10.1rc812.dist-info → qontract_reconcile-0.10.1rc814.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.1rc812.dist-info → qontract_reconcile-0.10.1rc814.dist-info}/entry_points.txt +0 -0
- {qontract_reconcile-0.10.1rc812.dist-info → qontract_reconcile-0.10.1rc814.dist-info}/top_level.txt +0 -0
{qontract_reconcile-0.10.1rc812.dist-info → qontract_reconcile-0.10.1rc814.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: qontract-reconcile
|
3
|
-
Version: 0.10.
|
3
|
+
Version: 0.10.1rc814
|
4
4
|
Summary: Collection of tools to reconcile services with their desired state as defined in the app-interface DB.
|
5
5
|
Home-page: https://github.com/app-sre/qontract-reconcile
|
6
6
|
Author: Red Hat App-SRE Team
|
{qontract_reconcile-0.10.1rc812.dist-info → qontract_reconcile-0.10.1rc814.dist-info}/RECORD
RENAMED
@@ -319,7 +319,7 @@ reconcile/gql_definitions/ldap_groups/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCe
|
|
319
319
|
reconcile/gql_definitions/ldap_groups/roles.py,sha256=goGDnkBBFy0mdLsXqL9qlSLPCFd9rwiD1rrsIH-6nZQ,2888
|
320
320
|
reconcile/gql_definitions/ldap_groups/settings.py,sha256=KR6eKqXQWVYZAUEdatL1RCARaTOWl9X-QmxEMVjVNDE,2227
|
321
321
|
reconcile/gql_definitions/maintenance/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
322
|
-
reconcile/gql_definitions/maintenance/maintenances.py,sha256=
|
322
|
+
reconcile/gql_definitions/maintenance/maintenances.py,sha256=ObAoOHUrTxL1l8CUhdOACc1s49Pt5j1fsErXE49fs1g,3042
|
323
323
|
reconcile/gql_definitions/membershipsources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
324
324
|
reconcile/gql_definitions/membershipsources/roles.py,sha256=d3nv3GLsj_eKgwB1glsiK6smpC4i16WO3dU5rIdRg94,3678
|
325
325
|
reconcile/gql_definitions/ocm_labels/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -354,7 +354,7 @@ reconcile/gql_definitions/slo_documents/slo_documents.py,sha256=pOrm9NXAonlo6Lxq
|
|
354
354
|
reconcile/gql_definitions/status_board/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
355
355
|
reconcile/gql_definitions/status_board/status_board.py,sha256=vHEzncabujkqbjJ-ibMYNJTODgTc4DMf4y6TW3I_7II,4700
|
356
356
|
reconcile/gql_definitions/statuspage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
357
|
-
reconcile/gql_definitions/statuspage/statuspages.py,sha256=
|
357
|
+
reconcile/gql_definitions/statuspage/statuspages.py,sha256=fCTJCcXQkIuoSDLcW_kj85qioaEuk2mygiDcggakYUw,5171
|
358
358
|
reconcile/gql_definitions/templating/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
359
359
|
reconcile/gql_definitions/templating/template_collection.py,sha256=lS0vzEKV2ZrzOqOEriqpy0yBgKjb2Ftrzgx6PIH46_4,3310
|
360
360
|
reconcile/gql_definitions/templating/templates.py,sha256=ejAvQ13zfNMQTz3FWtRUic6dSvio3aAgBKEqt600hbk,2821
|
@@ -436,14 +436,14 @@ reconcile/skupper_network/models.py,sha256=DNTI7HZv-rqY42GIIxyRuvroHLvdH6rJerjIq
|
|
436
436
|
reconcile/skupper_network/reconciler.py,sha256=XS-1oKBr_1l3dYUAVqUH6gCHg1G5ZuOfY_7fgGVAiFA,9996
|
437
437
|
reconcile/skupper_network/site_controller.py,sha256=A3K-62BjJ5HiFVydV0ouGoD1NwrO7XhAH15BHAcS9fk,1550
|
438
438
|
reconcile/statuspage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
439
|
-
reconcile/statuspage/atlassian.py,sha256=
|
439
|
+
reconcile/statuspage/atlassian.py,sha256=odYbYX8l3oST7AYm66Q4MiaxSbvDzsqEO6VBYJggxkc,17420
|
440
440
|
reconcile/statuspage/integration.py,sha256=hsazrQMceJbr61nEkJLxJbHhudTGtFuH0mlCo66-2ug,711
|
441
|
-
reconcile/statuspage/page.py,sha256=
|
441
|
+
reconcile/statuspage/page.py,sha256=WHDwV2PXEo4WwI2EgPOkS6j_T7geZEDXTgSaqpDo75U,5101
|
442
442
|
reconcile/statuspage/state.py,sha256=HD9EOoKm_nEqCMLIwW809En3cq5VhyzKJPUbsh-bae8,1617
|
443
443
|
reconcile/statuspage/status.py,sha256=mfRJ_tW7jM4_Vy_1cc8C0fKJEoA2GwrA3gJeV1KImAw,2834
|
444
444
|
reconcile/statuspage/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
445
445
|
reconcile/statuspage/integrations/components.py,sha256=49KHd_E9AdRvcEA6n75q1McZv2LfN-hRsW-WA7dgw9g,2651
|
446
|
-
reconcile/statuspage/integrations/maintenances.py,sha256=
|
446
|
+
reconcile/statuspage/integrations/maintenances.py,sha256=7gmMS9HwORMWMMBtib6pNGLXR6sAH2aW-HXKHJn6BN4,3083
|
447
447
|
reconcile/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
448
448
|
reconcile/templates/aws_access_key_email.j2,sha256=2MUr1ERmyISzKgHqsWYLd-1Wbl-peUa-FsGUS-JLUFc,238
|
449
449
|
reconcile/templates/email.yml.j2,sha256=OZgczNRgXPj2gVYTgwQyHAQrMGu7xp-e4W1rX19GcrU,690
|
@@ -779,7 +779,7 @@ tools/app_interface_metrics_exporter.py,sha256=zkwkxdAUAxjdc-pzx2_oJXG25fo0Fnyd5
|
|
779
779
|
tools/app_interface_reporter.py,sha256=upA-J-n-HXHKVDINRuMR7vTt-iJvQORKUVi9D3leQto,17738
|
780
780
|
tools/glitchtip_access_reporter.py,sha256=oPBnk_YoDuljU3v0FaChzOwwnk4vap1xEE67QEjzdqs,2948
|
781
781
|
tools/glitchtip_access_revalidation.py,sha256=8kbBJk04mkq28kWoRDDkfCGIF3GRg3pJrFAh1sW0dbk,2821
|
782
|
-
tools/qontract_cli.py,sha256=
|
782
|
+
tools/qontract_cli.py,sha256=PiT1dKAegpt_S-rALEQNEG7CKcgY5sPOY6haZEnxBzs,114860
|
783
783
|
tools/sd_app_sre_alert_report.py,sha256=e9vAdyenUz2f5c8-z-5WY0wv-SJ9aePKDH2r4IwB6pc,5063
|
784
784
|
tools/template_validation.py,sha256=-U-lTGeLaci8yWPEblCJeev2DOlY1jM9QOOh-O1zts8,3376
|
785
785
|
tools/cli_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -806,8 +806,8 @@ tools/test/test_app_interface_metrics_exporter.py,sha256=SX7qL3D1SIRKFo95FoQztvf
|
|
806
806
|
tools/test/test_qontract_cli.py,sha256=_D61RFGAN5x44CY1tYbouhlGXXABwYfxKSWSQx3Jrss,4941
|
807
807
|
tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
|
808
808
|
tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
|
809
|
-
qontract_reconcile-0.10.
|
810
|
-
qontract_reconcile-0.10.
|
811
|
-
qontract_reconcile-0.10.
|
812
|
-
qontract_reconcile-0.10.
|
813
|
-
qontract_reconcile-0.10.
|
809
|
+
qontract_reconcile-0.10.1rc814.dist-info/METADATA,sha256=izBuRBYFxzthBAfeBBKkMgqw9MspsZkQJ5HzbfT5tRA,2314
|
810
|
+
qontract_reconcile-0.10.1rc814.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
811
|
+
qontract_reconcile-0.10.1rc814.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
|
812
|
+
qontract_reconcile-0.10.1rc814.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
|
813
|
+
qontract_reconcile-0.10.1rc814.dist-info/RECORD,,
|
@@ -34,6 +34,9 @@ query Maintenances {
|
|
34
34
|
page {
|
35
35
|
name
|
36
36
|
}
|
37
|
+
remindSubscribers
|
38
|
+
notifySubscribersOnStart
|
39
|
+
notifySubscribersOnCompletion
|
37
40
|
}
|
38
41
|
}
|
39
42
|
}
|
@@ -61,6 +64,9 @@ class StatusPageV1(ConfiguredBaseModel):
|
|
61
64
|
|
62
65
|
class MaintenanceStatuspageAnnouncementV1(MaintenanceAnnouncementV1):
|
63
66
|
page: StatusPageV1 = Field(..., alias="page")
|
67
|
+
remind_subscribers: Optional[bool] = Field(..., alias="remindSubscribers")
|
68
|
+
notify_subscribers_on_start: Optional[bool] = Field(..., alias="notifySubscribersOnStart")
|
69
|
+
notify_subscribers_on_completion: Optional[bool] = Field(..., alias="notifySubscribersOnCompletion")
|
64
70
|
|
65
71
|
|
66
72
|
class MaintenanceV1(ConfiguredBaseModel):
|
@@ -61,12 +61,18 @@ query StatusPages {
|
|
61
61
|
message
|
62
62
|
scheduledStart
|
63
63
|
scheduledEnd
|
64
|
+
affectedServices {
|
65
|
+
name
|
66
|
+
}
|
64
67
|
announcements {
|
65
68
|
provider
|
66
69
|
... on MaintenanceStatuspageAnnouncement_v1 {
|
67
70
|
page {
|
68
71
|
name
|
69
72
|
}
|
73
|
+
remindSubscribers
|
74
|
+
notifySubscribersOnStart
|
75
|
+
notifySubscribersOnCompletion
|
70
76
|
}
|
71
77
|
}
|
72
78
|
}
|
@@ -109,6 +115,10 @@ class StatusPageComponentV1(ConfiguredBaseModel):
|
|
109
115
|
status_config: Optional[list[Union[ManualStatusProviderV1, StatusProviderV1]]] = Field(..., alias="status_config")
|
110
116
|
|
111
117
|
|
118
|
+
class MaintenanceV1_AppV1(ConfiguredBaseModel):
|
119
|
+
name: str = Field(..., alias="name")
|
120
|
+
|
121
|
+
|
112
122
|
class MaintenanceAnnouncementV1(ConfiguredBaseModel):
|
113
123
|
provider: str = Field(..., alias="provider")
|
114
124
|
|
@@ -119,6 +129,9 @@ class MaintenanceStatuspageAnnouncementV1_StatusPageV1(ConfiguredBaseModel):
|
|
119
129
|
|
120
130
|
class MaintenanceStatuspageAnnouncementV1(MaintenanceAnnouncementV1):
|
121
131
|
page: MaintenanceStatuspageAnnouncementV1_StatusPageV1 = Field(..., alias="page")
|
132
|
+
remind_subscribers: Optional[bool] = Field(..., alias="remindSubscribers")
|
133
|
+
notify_subscribers_on_start: Optional[bool] = Field(..., alias="notifySubscribersOnStart")
|
134
|
+
notify_subscribers_on_completion: Optional[bool] = Field(..., alias="notifySubscribersOnCompletion")
|
122
135
|
|
123
136
|
|
124
137
|
class MaintenanceV1(ConfiguredBaseModel):
|
@@ -126,6 +139,7 @@ class MaintenanceV1(ConfiguredBaseModel):
|
|
126
139
|
message: str = Field(..., alias="message")
|
127
140
|
scheduled_start: str = Field(..., alias="scheduledStart")
|
128
141
|
scheduled_end: str = Field(..., alias="scheduledEnd")
|
142
|
+
affected_services: list[MaintenanceV1_AppV1] = Field(..., alias="affectedServices")
|
129
143
|
announcements: Optional[list[Union[MaintenanceStatuspageAnnouncementV1, MaintenanceAnnouncementV1]]] = Field(..., alias="announcements")
|
130
144
|
|
131
145
|
|
@@ -15,6 +15,7 @@ from reconcile.gql_definitions.statuspage.statuspages import StatusPageV1
|
|
15
15
|
from reconcile.statuspage.page import (
|
16
16
|
StatusComponent,
|
17
17
|
StatusMaintenance,
|
18
|
+
StatusMaintenanceAnnouncement,
|
18
19
|
StatusPage,
|
19
20
|
)
|
20
21
|
from reconcile.statuspage.state import ComponentBindingState
|
@@ -54,6 +55,10 @@ class AtlassianRawMaintenance(BaseModel):
|
|
54
55
|
scheduled_for: str
|
55
56
|
scheduled_until: str
|
56
57
|
incident_updates: list[AtlassianRawMaintenanceUpdate]
|
58
|
+
components: list[AtlassianRawComponent]
|
59
|
+
auto_transition_deliver_notifications_at_end: bool
|
60
|
+
auto_transition_deliver_notifications_at_start: bool
|
61
|
+
scheduled_remind_prior: bool
|
57
62
|
|
58
63
|
|
59
64
|
class AtlassianAPI:
|
@@ -121,6 +126,11 @@ class AtlassianAPI:
|
|
121
126
|
all_scheduled_incidents = self._list_items(url)
|
122
127
|
return [AtlassianRawMaintenance(**i) for i in all_scheduled_incidents]
|
123
128
|
|
129
|
+
def list_active_maintenances(self) -> list[AtlassianRawMaintenance]:
|
130
|
+
url = f"{self.api_url}/v1/pages/{self.page_id}/incidents/active_maintenance"
|
131
|
+
all_active_incidents = self._list_items(url)
|
132
|
+
return [AtlassianRawMaintenance(**i) for i in all_active_incidents]
|
133
|
+
|
124
134
|
def create_incident(self, data: dict[str, Any]) -> str:
|
125
135
|
url = f"{self.api_url}/v1/pages/{self.page_id}/incidents"
|
126
136
|
response = requests.post(
|
@@ -190,6 +200,26 @@ class AtlassianStatusPageProvider:
|
|
190
200
|
components=[c for c in components if c is not None],
|
191
201
|
)
|
192
202
|
|
203
|
+
def _raw_component_to_status_component(
|
204
|
+
self, raw_component: AtlassianRawComponent, name_override: Optional[str] = None
|
205
|
+
) -> StatusComponent:
|
206
|
+
group_name = (
|
207
|
+
self._group_id_to_name.get(raw_component.group_id)
|
208
|
+
if raw_component.group_id
|
209
|
+
else None
|
210
|
+
)
|
211
|
+
return StatusComponent(
|
212
|
+
name=name_override or raw_component.name,
|
213
|
+
display_name=raw_component.name,
|
214
|
+
description=raw_component.description,
|
215
|
+
group_name=group_name,
|
216
|
+
status_provider_configs=[
|
217
|
+
ManualStatusProvider(
|
218
|
+
component_status=raw_component.status,
|
219
|
+
)
|
220
|
+
],
|
221
|
+
)
|
222
|
+
|
193
223
|
def _bound_raw_component_to_status_component(
|
194
224
|
self, raw_component: AtlassianRawComponent
|
195
225
|
) -> Optional[StatusComponent]:
|
@@ -197,21 +227,8 @@ class AtlassianStatusPageProvider:
|
|
197
227
|
raw_component.id
|
198
228
|
)
|
199
229
|
if bound_component_name:
|
200
|
-
|
201
|
-
|
202
|
-
if raw_component.group_id
|
203
|
-
else None
|
204
|
-
)
|
205
|
-
return StatusComponent(
|
206
|
-
name=bound_component_name,
|
207
|
-
display_name=raw_component.name,
|
208
|
-
description=raw_component.description,
|
209
|
-
group_name=group_name,
|
210
|
-
status_provider_configs=[
|
211
|
-
ManualStatusProvider(
|
212
|
-
component_status=raw_component.status,
|
213
|
-
)
|
214
|
-
],
|
230
|
+
return self._raw_component_to_status_component(
|
231
|
+
raw_component, name_override=bound_component_name
|
215
232
|
)
|
216
233
|
return None
|
217
234
|
|
@@ -332,7 +349,13 @@ class AtlassianStatusPageProvider:
|
|
332
349
|
# resolve status
|
333
350
|
desired_component_status = desired.desired_component_status()
|
334
351
|
if desired_component_status:
|
335
|
-
|
352
|
+
active_maintenance_affecting_component = [
|
353
|
+
m
|
354
|
+
for m in self.active_maintenances
|
355
|
+
if desired.display_name in [c.name for c in m.components]
|
356
|
+
]
|
357
|
+
if not active_maintenance_affecting_component:
|
358
|
+
component_update["status"] = desired_component_status
|
336
359
|
|
337
360
|
if current_component:
|
338
361
|
logging.info(f"update component {desired.name}: {component_update}")
|
@@ -377,6 +400,27 @@ class AtlassianStatusPageProvider:
|
|
377
400
|
if not dry_run:
|
378
401
|
self._binding_state.bind_component(component_name, component_id)
|
379
402
|
|
403
|
+
def _raw_maintenance_to_status_maintenance(
|
404
|
+
self,
|
405
|
+
raw_maintenance: AtlassianRawMaintenance,
|
406
|
+
name_override: Optional[str] = None,
|
407
|
+
) -> StatusMaintenance:
|
408
|
+
return StatusMaintenance(
|
409
|
+
name=name_override or raw_maintenance.name,
|
410
|
+
message=raw_maintenance.incident_updates[0].body,
|
411
|
+
schedule_start=raw_maintenance.scheduled_for,
|
412
|
+
schedule_end=raw_maintenance.scheduled_until,
|
413
|
+
components=[
|
414
|
+
self._raw_component_to_status_component(c)
|
415
|
+
for c in raw_maintenance.components
|
416
|
+
],
|
417
|
+
announcements=StatusMaintenanceAnnouncement(
|
418
|
+
remind_subscribers=raw_maintenance.scheduled_remind_prior,
|
419
|
+
notify_subscribers_on_start=raw_maintenance.auto_transition_deliver_notifications_at_start,
|
420
|
+
notify_subscribers_on_completion=raw_maintenance.auto_transition_deliver_notifications_at_end,
|
421
|
+
),
|
422
|
+
)
|
423
|
+
|
380
424
|
@classmethod
|
381
425
|
def init_from_page(
|
382
426
|
cls,
|
@@ -398,17 +442,19 @@ class AtlassianStatusPageProvider:
|
|
398
442
|
)
|
399
443
|
|
400
444
|
@property
|
401
|
-
def
|
445
|
+
def scheduled_maintenances(self) -> list[StatusMaintenance]:
|
402
446
|
return [
|
403
|
-
|
404
|
-
name=m.name,
|
405
|
-
message=m.incident_updates[0].body,
|
406
|
-
schedule_start=m.scheduled_for,
|
407
|
-
schedule_end=m.scheduled_until,
|
408
|
-
)
|
447
|
+
self._raw_maintenance_to_status_maintenance(m)
|
409
448
|
for m in self._api.list_scheduled_maintenances()
|
410
449
|
]
|
411
450
|
|
451
|
+
@property
|
452
|
+
def active_maintenances(self) -> list[StatusMaintenance]:
|
453
|
+
return [
|
454
|
+
self._raw_maintenance_to_status_maintenance(m)
|
455
|
+
for m in self._api.list_active_maintenances()
|
456
|
+
]
|
457
|
+
|
412
458
|
def create_maintenance(self, maintenance: StatusMaintenance) -> None:
|
413
459
|
data = {
|
414
460
|
"name": maintenance.name,
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import logging
|
2
2
|
import sys
|
3
|
+
from datetime import datetime, timezone
|
3
4
|
|
4
5
|
from reconcile.statuspage.atlassian import AtlassianStatusPageProvider
|
5
6
|
from reconcile.statuspage.integration import get_binding_state, get_status_pages
|
@@ -46,23 +47,32 @@ class StatusPageMaintenancesIntegration(QontractReconcileIntegration[NoParams]):
|
|
46
47
|
def run(self, dry_run: bool = False) -> None:
|
47
48
|
binding_state = get_binding_state(self.name, self.secret_reader)
|
48
49
|
pages = get_status_pages()
|
50
|
+
now = datetime.now(timezone.utc)
|
49
51
|
|
50
52
|
error = False
|
51
53
|
for p in pages:
|
52
54
|
try:
|
53
55
|
desired_state = [
|
54
|
-
StatusMaintenance.init_from_maintenance(
|
56
|
+
StatusMaintenance.init_from_maintenance(
|
57
|
+
m, page_components=p.components or []
|
58
|
+
)
|
55
59
|
for m in p.maintenances or []
|
60
|
+
if datetime.fromisoformat(m.scheduled_start) > now
|
56
61
|
]
|
57
62
|
page_provider = AtlassianStatusPageProvider.init_from_page(
|
58
63
|
page=p,
|
59
64
|
token=self.secret_reader.read_secret(p.credentials),
|
60
65
|
component_binding_state=binding_state,
|
61
66
|
)
|
67
|
+
current_state = [
|
68
|
+
m
|
69
|
+
for m in page_provider.scheduled_maintenances
|
70
|
+
if page_provider.has_component_binding_for(m.name)
|
71
|
+
]
|
62
72
|
self.reconcile(
|
63
73
|
dry_run=dry_run,
|
64
74
|
desired_state=desired_state,
|
65
|
-
current_state=
|
75
|
+
current_state=current_state,
|
66
76
|
provider=page_provider,
|
67
77
|
)
|
68
78
|
except Exception:
|
reconcile/statuspage/page.py
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
-
from typing import Optional, Self
|
1
|
+
from typing import Optional, Self, cast
|
2
2
|
|
3
3
|
from pydantic import BaseModel
|
4
4
|
|
5
|
+
from reconcile.gql_definitions.maintenance.maintenances import (
|
6
|
+
MaintenanceStatuspageAnnouncementV1,
|
7
|
+
)
|
5
8
|
from reconcile.gql_definitions.statuspage.statuspages import (
|
6
9
|
MaintenanceV1,
|
7
10
|
StatusPageComponentV1,
|
@@ -12,6 +15,8 @@ from reconcile.statuspage.status import (
|
|
12
15
|
build_status_provider_config,
|
13
16
|
)
|
14
17
|
|
18
|
+
PROVIDER_NAME = "statuspage"
|
19
|
+
|
15
20
|
|
16
21
|
class StatusComponent(BaseModel):
|
17
22
|
"""
|
@@ -47,12 +52,14 @@ class StatusComponent(BaseModel):
|
|
47
52
|
arbitrary_types_allowed = True
|
48
53
|
|
49
54
|
@classmethod
|
50
|
-
def init_from_page_component(
|
55
|
+
def init_from_page_component(
|
56
|
+
cls, component: StatusPageComponentV1, name_override: Optional[str] = None
|
57
|
+
) -> Self:
|
51
58
|
status_configs = [
|
52
59
|
build_status_provider_config(cfg) for cfg in component.status_config or []
|
53
60
|
]
|
54
61
|
return cls(
|
55
|
-
name=component.name,
|
62
|
+
name=name_override or component.name,
|
56
63
|
display_name=component.display_name,
|
57
64
|
description=component.description,
|
58
65
|
group_name=component.group_name,
|
@@ -94,6 +101,26 @@ class StatusPage(BaseModel):
|
|
94
101
|
)
|
95
102
|
|
96
103
|
|
104
|
+
class StatusMaintenanceAnnouncement(BaseModel):
|
105
|
+
"""
|
106
|
+
Represents the desired state of a status maintenance.
|
107
|
+
"""
|
108
|
+
|
109
|
+
remind_subscribers: Optional[bool] = None
|
110
|
+
notify_subscribers_on_start: Optional[bool] = None
|
111
|
+
notify_subscribers_on_completion: Optional[bool] = None
|
112
|
+
|
113
|
+
@classmethod
|
114
|
+
def init_from_announcement(
|
115
|
+
cls, announcement: MaintenanceStatuspageAnnouncementV1
|
116
|
+
) -> Self:
|
117
|
+
return cls(
|
118
|
+
remind_subscribers=announcement.remind_subscribers,
|
119
|
+
notify_subscribers_on_start=announcement.notify_subscribers_on_start,
|
120
|
+
notify_subscribers_on_completion=announcement.notify_subscribers_on_completion,
|
121
|
+
)
|
122
|
+
|
123
|
+
|
97
124
|
class StatusMaintenance(BaseModel):
|
98
125
|
"""
|
99
126
|
Represents the desired state of a status maintenance.
|
@@ -103,12 +130,37 @@ class StatusMaintenance(BaseModel):
|
|
103
130
|
message: str
|
104
131
|
schedule_start: str
|
105
132
|
schedule_end: str
|
133
|
+
components: list[StatusComponent]
|
134
|
+
announcements: StatusMaintenanceAnnouncement
|
106
135
|
|
107
136
|
@classmethod
|
108
|
-
def init_from_maintenance(
|
137
|
+
def init_from_maintenance(
|
138
|
+
cls,
|
139
|
+
maintenance: MaintenanceV1,
|
140
|
+
page_components: list[StatusPageComponentV1],
|
141
|
+
) -> Self:
|
142
|
+
affected_services = [a.name for a in maintenance.affected_services]
|
143
|
+
affected_components = [
|
144
|
+
StatusComponent.init_from_page_component(c, name_override=c.display_name)
|
145
|
+
for c in page_components
|
146
|
+
if c.app.name in affected_services
|
147
|
+
]
|
148
|
+
statuspage_announcements = [
|
149
|
+
StatusMaintenanceAnnouncement.init_from_announcement(
|
150
|
+
cast(MaintenanceStatuspageAnnouncementV1, m)
|
151
|
+
)
|
152
|
+
for m in maintenance.announcements or []
|
153
|
+
if m.provider == PROVIDER_NAME
|
154
|
+
]
|
155
|
+
if len(statuspage_announcements) != 1:
|
156
|
+
raise ValueError(
|
157
|
+
f"Maintenanace announcements must include exactly one item of provider {PROVIDER_NAME}"
|
158
|
+
)
|
109
159
|
return cls(
|
110
160
|
name=maintenance.name,
|
111
161
|
message=maintenance.message.rstrip("\n"),
|
112
162
|
schedule_start=maintenance.scheduled_start,
|
113
163
|
schedule_end=maintenance.scheduled_end,
|
164
|
+
components=affected_components,
|
165
|
+
announcements=statuspage_announcements[0],
|
114
166
|
)
|
tools/qontract_cli.py
CHANGED
@@ -2597,7 +2597,7 @@ def maintenances(ctx):
|
|
2597
2597
|
"services": ", ".join(a.name for a in m.affected_services),
|
2598
2598
|
}
|
2599
2599
|
for m in maintenances
|
2600
|
-
if datetime.fromisoformat(m.
|
2600
|
+
if datetime.fromisoformat(m.scheduled_start) > now
|
2601
2601
|
]
|
2602
2602
|
columns = [
|
2603
2603
|
"name",
|
File without changes
|
File without changes
|
{qontract_reconcile-0.10.1rc812.dist-info → qontract_reconcile-0.10.1rc814.dist-info}/top_level.txt
RENAMED
File without changes
|