gitops 1.1.1__tar.gz → 1.2.0__tar.gz
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.
- gitops-1.2.0/.release-please-manifest.json +5 -0
- {gitops-1.1.1 → gitops-1.2.0}/CHANGELOG.md +20 -0
- {gitops-1.1.1 → gitops-1.2.0}/PKG-INFO +1 -1
- {gitops-1.1.1 → gitops-1.2.0}/charts/gitops/CHANGELOG.md +7 -0
- {gitops-1.1.1 → gitops-1.2.0}/charts/gitops/Chart.yaml +1 -1
- {gitops-1.1.1 → gitops-1.2.0}/charts/gitops/templates/deployment.yml +0 -2
- {gitops-1.1.1 → gitops-1.2.0}/gitops/__init__.py +1 -1
- {gitops-1.1.1 → gitops-1.2.0}/gitops/utils/apps.py +15 -1
- {gitops-1.1.1 → gitops-1.2.0}/gitops_server/main.py +1 -4
- gitops-1.2.0/gitops_server/version.py +1 -0
- gitops-1.2.0/gitops_server/workers/__init__.py +1 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops_server/workers/deployer/deploy.py +52 -24
- {gitops-1.1.1 → gitops-1.2.0}/gitops_server/workers/deployer/hooks.py +4 -1
- {gitops-1.1.1 → gitops-1.2.0}/pyproject.toml +1 -1
- {gitops-1.1.1 → gitops-1.2.0}/tests/test_gitops_server.py +0 -11
- gitops-1.1.1/.release-please-manifest.json +0 -5
- gitops-1.1.1/gitops_server/version.py +0 -1
- gitops-1.1.1/gitops_server/workers/__init__.py +0 -2
- gitops-1.1.1/gitops_server/workers/status_updater/__init__.py +0 -1
- gitops-1.1.1/gitops_server/workers/status_updater/worker.py +0 -126
- {gitops-1.1.1 → gitops-1.2.0}/.dockerignore +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/.github/workflows/release-please.yaml +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/.github/workflows/test.yml +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/.gitignore +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/.mise.toml +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/.pre-commit-config.yaml +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/Dockerfile +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/LICENSE +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/README.md +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/charts/gitops/.helmignore +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/charts/gitops/README.md +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/charts/gitops/templates/_helpers.tpl +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/charts/gitops/templates/certificate.yml +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/charts/gitops/templates/environment.yml +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/charts/gitops/templates/ingress.yml +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/charts/gitops/templates/issuer.yml +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/charts/gitops/templates/secrets.yml +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/charts/gitops/templates/service.yml +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/charts/gitops/templates/serviceaccount.yaml +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/charts/gitops/values.yaml +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/docker-compose.yml +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops/common/__init__.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops/common/app.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops/common/utils.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops/core.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops/main.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops/py.typed +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops/settings.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops/shorthands.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops/utils/__init__.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops/utils/async_runner.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops/utils/cli.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops/utils/exceptions.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops/utils/images.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops/utils/kube.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops/utils/tags.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops/utils/yaml.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops_server/CHANGELOG.md +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops_server/README.md +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops_server/__init__.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops_server/app.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops_server/settings.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops_server/types.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops_server/utils/__init__.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops_server/utils/git.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops_server/utils/github.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops_server/utils/slack.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops_server/workers/deployer/__init__.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/gitops_server/workers/deployer/worker.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/release-please-config.json +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/release.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/ruff.toml +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/secrets.example.env +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/setup.cfg +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/tests/__init__.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/tests/conftest.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/tests/sample_data.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/tests/test_app.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/tests/test_core.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/tests/test_deploy.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/tests/test_git.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/tests/test_utils.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/tests/utils.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/tests/webhook_sample_data.py +0 -0
- {gitops-1.1.1 → gitops-1.2.0}/uv.lock +0 -0
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.2.0](https://github.com/uptick/gitops/compare/gitops-v1.1.2...gitops-v1.2.0) (2025-03-17)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **cli:** allow filtering apps via a csv file ([f465d9a](https://github.com/uptick/gitops/commit/f465d9a8ad565afde1f9bf7f9d1232281352882c))
|
|
9
|
+
* **cli:** allow filtering apps via a csv file ([7fa06cd](https://github.com/uptick/gitops/commit/7fa06cd6d8ca42a2d1e50304ad7cba7e688650e9))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* remove status updater and replace with a post deploy hook ([e72c2ad](https://github.com/uptick/gitops/commit/e72c2ad2b46304f2258d7b3d1bb04e4200ce1602))
|
|
15
|
+
|
|
16
|
+
## [1.1.2](https://github.com/uptick/gitops/compare/gitops-v1.1.1...gitops-v1.1.2) (2025-02-14)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Bug Fixes
|
|
20
|
+
|
|
21
|
+
* **helm:** if a roll back is required; rollback before upgrading ([762cdb3](https://github.com/uptick/gitops/commit/762cdb388162c6b8645ec01f0d1c94c20d948b9f))
|
|
22
|
+
|
|
3
23
|
## [1.1.1](https://github.com/uptick/gitops/compare/gitops-v1.1.0...gitops-v1.1.1) (2025-01-24)
|
|
4
24
|
|
|
5
25
|
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.1.2](https://github.com/uptick/gitops/compare/helm-v1.1.1...helm-v1.1.2) (2025-02-14)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* remove gitops resource limits ([fcd9680](https://github.com/uptick/gitops/commit/fcd9680f6e94e5cb7a286c269aeaa1626c95a181))
|
|
9
|
+
|
|
3
10
|
## [1.1.1](https://github.com/uptick/gitops/compare/helm-v1.1.0...helm-v1.1.1) (2025-01-24)
|
|
4
11
|
|
|
5
12
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import pathlib
|
|
1
2
|
import sys
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
from typing import Literal
|
|
@@ -77,6 +78,9 @@ def get_apps( # noqa: C901
|
|
|
77
78
|
`exclude`. The incoming filter and exclude params may come in as a list or commastring.
|
|
78
79
|
For the purpose of this filtering, app names and image tag prefixes are also considered as
|
|
79
80
|
tags. For instance, you can do get_apps(tags=[emeriss, production], exclude=[arafire]).
|
|
81
|
+
|
|
82
|
+
If filter is a file like name; read a list of app names from the file.
|
|
83
|
+
|
|
80
84
|
Calling this method without any args returns all apps.
|
|
81
85
|
There are three modes for communicating selected apps to the user:
|
|
82
86
|
- PROMPT: Prints selected apps and asks for confirmation to proceed.
|
|
@@ -84,8 +88,13 @@ def get_apps( # noqa: C901
|
|
|
84
88
|
- SILENT: Proceeds without printing.
|
|
85
89
|
Apps with the `inactive` tag are excluded by default, unless requested otherwise.
|
|
86
90
|
"""
|
|
91
|
+
app_names: list[str] = []
|
|
92
|
+
|
|
87
93
|
if filter == "all":
|
|
88
94
|
filter = set()
|
|
95
|
+
elif isinstance(filter, str) and pathlib.Path(filter).is_file():
|
|
96
|
+
app_names = [f.strip() for f in pathlib.Path(filter).read_text().splitlines() if f.strip()]
|
|
97
|
+
filter = set()
|
|
89
98
|
else:
|
|
90
99
|
filter = set(filter.split(",") if filter and isinstance(filter, str) else filter)
|
|
91
100
|
|
|
@@ -114,7 +123,12 @@ def get_apps( # noqa: C901
|
|
|
114
123
|
|
|
115
124
|
tags = set(app.tags + pseudotags)
|
|
116
125
|
existing_tags |= tags
|
|
117
|
-
|
|
126
|
+
|
|
127
|
+
# Decide whether to include the app based on the filter and exclude tags.
|
|
128
|
+
if app_names:
|
|
129
|
+
if app.name in app_names:
|
|
130
|
+
apps.append(app)
|
|
131
|
+
elif filter <= tags and not exclude & tags:
|
|
118
132
|
apps.append(app)
|
|
119
133
|
|
|
120
134
|
validate_tags(filter | exclude, existing_tags)
|
|
@@ -13,7 +13,7 @@ from uptick_observability.logging import ( # type: ignore[import-untyped]
|
|
|
13
13
|
|
|
14
14
|
from gitops_server import settings
|
|
15
15
|
from gitops_server.app import app
|
|
16
|
-
from gitops_server.workers import
|
|
16
|
+
from gitops_server.workers import DeployQueueWorker
|
|
17
17
|
|
|
18
18
|
manually_instrument_logging()
|
|
19
19
|
manually_instrument_fastapi()
|
|
@@ -64,9 +64,6 @@ async def startup_event():
|
|
|
64
64
|
deploy_queue_worker = DeployQueueWorker.get_worker()
|
|
65
65
|
deploy_queue_worker.task = asyncio.ensure_future(deploy_queue_worker.run(), loop=loop)
|
|
66
66
|
|
|
67
|
-
deployment_status_worker = DeploymentStatusWorker.get_worker()
|
|
68
|
-
deployment_status_worker.task = asyncio.ensure_future(deployment_status_worker.run(), loop=loop)
|
|
69
|
-
|
|
70
67
|
|
|
71
68
|
def get_digest(data: bytes) -> str:
|
|
72
69
|
"""Calculate the digest of a webhook body.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.2.0"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .deployer import DeployQueueWorker # NOQA
|
|
@@ -11,7 +11,7 @@ from opentelemetry import trace
|
|
|
11
11
|
|
|
12
12
|
from gitops.common.app import App
|
|
13
13
|
from gitops_server import settings
|
|
14
|
-
from gitops_server.types import AppDefinitions, UpdateAppResult
|
|
14
|
+
from gitops_server.types import AppDefinitions, RunOutput, UpdateAppResult
|
|
15
15
|
from gitops_server.utils import get_repo_name_from_url, github, run, slack
|
|
16
16
|
from gitops_server.utils.git import temp_repo
|
|
17
17
|
|
|
@@ -133,6 +133,17 @@ class Deployer:
|
|
|
133
133
|
)
|
|
134
134
|
return update_result
|
|
135
135
|
|
|
136
|
+
async def rollback_deployment(self, app: App) -> None:
|
|
137
|
+
with tracer.start_as_current_span("rollback_deployment", attributes={"app": app.name}):
|
|
138
|
+
logger.warning(
|
|
139
|
+
"Rolling back %s deployment due to previous failed helm install",
|
|
140
|
+
app.name,
|
|
141
|
+
)
|
|
142
|
+
await run(
|
|
143
|
+
f"helm rollback --namespace={app.namespace} {app.name}",
|
|
144
|
+
suppress_errors=True,
|
|
145
|
+
)
|
|
146
|
+
|
|
136
147
|
async def update_app_deployment(self, app: App) -> UpdateAppResult | None:
|
|
137
148
|
app.set_value("deployment.labels.gitops/deploy_id", self.deploy_id)
|
|
138
149
|
app.set_value("deployment.labels.gitops/status", github.STATUSES.in_progress)
|
|
@@ -155,6 +166,40 @@ class Deployer:
|
|
|
155
166
|
os.fsync(cfg.fileno())
|
|
156
167
|
|
|
157
168
|
with tracer.start_as_current_span("helm_upgrade"):
|
|
169
|
+
|
|
170
|
+
async def upgrade_helm_git() -> RunOutput:
|
|
171
|
+
result = await run(
|
|
172
|
+
"helm secrets upgrade --create-namespace"
|
|
173
|
+
f" --history-max {MAX_HELM_HISTORY}"
|
|
174
|
+
" --install"
|
|
175
|
+
" --timeout=600s"
|
|
176
|
+
f"{' --set skip_migrations=true' if self.skip_migrations else ''}"
|
|
177
|
+
f" -f {cfg.name}"
|
|
178
|
+
f" --namespace={app.namespace}"
|
|
179
|
+
f" {app.name}"
|
|
180
|
+
f" {chart_folder_path}",
|
|
181
|
+
suppress_errors=True,
|
|
182
|
+
)
|
|
183
|
+
return result
|
|
184
|
+
|
|
185
|
+
result = await upgrade_helm_git()
|
|
186
|
+
if result["exit_code"] != 0 and "is in progress" in result["output"]:
|
|
187
|
+
await self.rollback_deployment(app)
|
|
188
|
+
result = await upgrade_helm_git()
|
|
189
|
+
|
|
190
|
+
elif app.chart.type == "helm":
|
|
191
|
+
span.set_attribute("gitops.chart.type", "helm")
|
|
192
|
+
with tempfile.NamedTemporaryFile(suffix=".yml") as cfg:
|
|
193
|
+
cfg.write(json.dumps(app.values).encode())
|
|
194
|
+
cfg.flush()
|
|
195
|
+
os.fsync(cfg.fileno())
|
|
196
|
+
chart_version_arguments = f" --version={app.chart.version}" if app.chart.version else ""
|
|
197
|
+
with tracer.start_as_current_span("helm_repo_add"):
|
|
198
|
+
await run(f"helm repo add {app.chart.helm_repo} {app.chart.helm_repo_url}")
|
|
199
|
+
|
|
200
|
+
with tracer.start_as_current_span("helm_upgrade"):
|
|
201
|
+
|
|
202
|
+
async def upgrade_helm_chart() -> RunOutput:
|
|
158
203
|
result = await run(
|
|
159
204
|
"helm secrets upgrade --create-namespace"
|
|
160
205
|
f" --history-max {MAX_HELM_HISTORY}"
|
|
@@ -164,32 +209,15 @@ class Deployer:
|
|
|
164
209
|
f" -f {cfg.name}"
|
|
165
210
|
f" --namespace={app.namespace}"
|
|
166
211
|
f" {app.name}"
|
|
167
|
-
f" {
|
|
212
|
+
f" {app.chart.helm_chart} {chart_version_arguments}",
|
|
168
213
|
suppress_errors=True,
|
|
169
214
|
)
|
|
170
|
-
|
|
171
|
-
span.set_attribute("gitops.chart.type", "helm")
|
|
172
|
-
with tempfile.NamedTemporaryFile(suffix=".yml") as cfg:
|
|
173
|
-
cfg.write(json.dumps(app.values).encode())
|
|
174
|
-
cfg.flush()
|
|
175
|
-
os.fsync(cfg.fileno())
|
|
176
|
-
chart_version_arguments = f" --version={app.chart.version}" if app.chart.version else ""
|
|
177
|
-
with tracer.start_as_current_span("helm_repo_add"):
|
|
178
|
-
await run(f"helm repo add {app.chart.helm_repo} {app.chart.helm_repo_url}")
|
|
215
|
+
return result
|
|
179
216
|
|
|
180
|
-
|
|
181
|
-
result
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
" --install"
|
|
185
|
-
" --timeout=600s"
|
|
186
|
-
f"{' --set skip_migrations=true' if self.skip_migrations else ''}"
|
|
187
|
-
f" -f {cfg.name}"
|
|
188
|
-
f" --namespace={app.namespace}"
|
|
189
|
-
f" {app.name}"
|
|
190
|
-
f" {app.chart.helm_chart} {chart_version_arguments}",
|
|
191
|
-
suppress_errors=True,
|
|
192
|
-
)
|
|
217
|
+
result = await upgrade_helm_chart()
|
|
218
|
+
if result["exit_code"] != 0 and "is in progress" in result["output"]:
|
|
219
|
+
await self.rollback_deployment(app)
|
|
220
|
+
result = await upgrade_helm_chart()
|
|
193
221
|
else:
|
|
194
222
|
logger.warning("Local is not implemented yet")
|
|
195
223
|
return None
|
|
@@ -50,10 +50,13 @@ async def update_issue_from_deployment_url(app: App, deployment_url: str, **kwar
|
|
|
50
50
|
|
|
51
51
|
async def handle_successful_deploy(app: App, result, deployer, **kwargs) -> UpdateAppResult:
|
|
52
52
|
github_deployment_url = str(app.values.get("github/deployment_url", ""))
|
|
53
|
+
# I know this shouldn't be uptick specific but for now it is.
|
|
54
|
+
environment_url = app.values.get("github/environment_url", "") or f"https://{app.name}.onuptick.com"
|
|
53
55
|
await github.update_deployment(
|
|
54
56
|
github_deployment_url,
|
|
55
|
-
status=github.STATUSES.
|
|
57
|
+
status=github.STATUSES.success,
|
|
56
58
|
description="Helm installed app into cluster. Waiting for pods to deploy.",
|
|
59
|
+
environment_url=environment_url,
|
|
57
60
|
)
|
|
58
61
|
return result
|
|
59
62
|
|
|
@@ -31,14 +31,3 @@ def test_webhook_returns_200_if_hmac_is_correct():
|
|
|
31
31
|
|
|
32
32
|
assert response.status_code == 200
|
|
33
33
|
get_worker_mock.assert_called()
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
@patch("gitops_server.main.settings.GITHUB_WEBHOOK_KEY", "test_key")
|
|
37
|
-
def test_webhook_returns_400_if_hmac_is_invalid():
|
|
38
|
-
sha_encoding = "INVALID HMAC ENCODING"
|
|
39
|
-
headers["X-Hub-Signature"] = f"sha1={sha_encoding}"
|
|
40
|
-
|
|
41
|
-
with patch.object(gitops_server.workers.DeploymentStatusWorker, "get_worker", AsyncMock):
|
|
42
|
-
response = client.post("/webhook", headers=headers, json=payload)
|
|
43
|
-
|
|
44
|
-
assert response.status_code == 400
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.1.1"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .worker import DeploymentStatusWorker # NOQA
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
This async worker keeps tracks of ongoing deployments by polling k8s space (CLUSTER_NAMESPACE).
|
|
3
|
-
|
|
4
|
-
The worker polls and checks for deployments with the following labels: gitops/deploy_id and gitops/status=in_progress
|
|
5
|
-
|
|
6
|
-
Any failing/successfully deployed deployment is notified to github.
|
|
7
|
-
This mechanism will only work if the annotation: github/deployment_url is applied to the deployment.
|
|
8
|
-
|
|
9
|
-
TODO
|
|
10
|
-
- Update the slack summary message from deploy.py
|
|
11
|
-
- @notify the user if the deployment failed
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
import asyncio
|
|
15
|
-
import logging
|
|
16
|
-
|
|
17
|
-
import kubernetes_asyncio # type:ignore[import-untyped]
|
|
18
|
-
import kubernetes_asyncio.client # type:ignore[import-untyped]
|
|
19
|
-
import kubernetes_asyncio.config # type:ignore[import-untyped]
|
|
20
|
-
|
|
21
|
-
from gitops_server.settings import CLUSTER_NAMESPACE
|
|
22
|
-
from gitops_server.utils import github
|
|
23
|
-
|
|
24
|
-
logger = logging.getLogger("deployment_status")
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
async def get_ingress_url(api, namespace: str, app: str):
|
|
28
|
-
"""Attempts to get domain for the ingress associated with the app"""
|
|
29
|
-
ingresses = await kubernetes_asyncio.client.NetworkingV1Api(api).list_namespaced_ingress(
|
|
30
|
-
namespace=namespace, label_selector=f"app={app}"
|
|
31
|
-
)
|
|
32
|
-
environment_url = ""
|
|
33
|
-
if ingresses.items:
|
|
34
|
-
try:
|
|
35
|
-
environment_url = "https://" + ingresses.items[0].spec.rules[0].host
|
|
36
|
-
except Exception:
|
|
37
|
-
logger.warning(f"Could not find ingress for {app=}")
|
|
38
|
-
return environment_url
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
class DeploymentStatusWorker:
|
|
42
|
-
"""Watches for deployments and updates the github deployment status"""
|
|
43
|
-
|
|
44
|
-
_worker = None
|
|
45
|
-
|
|
46
|
-
@classmethod
|
|
47
|
-
def get_worker(cls):
|
|
48
|
-
if not cls._worker:
|
|
49
|
-
loop = asyncio.get_running_loop()
|
|
50
|
-
cls._worker = cls(loop)
|
|
51
|
-
return cls._worker
|
|
52
|
-
|
|
53
|
-
def __init__(self, loop):
|
|
54
|
-
self.loop = loop
|
|
55
|
-
|
|
56
|
-
async def load_config(self):
|
|
57
|
-
try:
|
|
58
|
-
kubernetes_asyncio.config.load_incluster_config()
|
|
59
|
-
except kubernetes_asyncio.config.config_exception.ConfigException:
|
|
60
|
-
await kubernetes_asyncio.config.load_kube_config()
|
|
61
|
-
|
|
62
|
-
async def process_work(self):
|
|
63
|
-
await asyncio.sleep(5)
|
|
64
|
-
async with kubernetes_asyncio.client.ApiClient() as api:
|
|
65
|
-
apps_api = kubernetes_asyncio.client.AppsV1Api(api)
|
|
66
|
-
deployments = await apps_api.list_namespaced_deployment(
|
|
67
|
-
# Only things that have gitops/deploy_id aka was deployed
|
|
68
|
-
namespace=CLUSTER_NAMESPACE,
|
|
69
|
-
label_selector="gitops/deploy_id,gitops/status=in_progress",
|
|
70
|
-
)
|
|
71
|
-
await asyncio.sleep(5)
|
|
72
|
-
|
|
73
|
-
for deployment in deployments.items:
|
|
74
|
-
# Deployment status may not exist yet
|
|
75
|
-
if not deployment.status:
|
|
76
|
-
continue
|
|
77
|
-
app = deployment.metadata.labels["app"]
|
|
78
|
-
namespace = deployment.metadata.namespace
|
|
79
|
-
github_deployment_url = deployment.metadata.annotations.get("github/deployment_url")
|
|
80
|
-
conds = {}
|
|
81
|
-
for x in deployment.status.conditions:
|
|
82
|
-
conds[x.type] = x
|
|
83
|
-
status = None
|
|
84
|
-
if (
|
|
85
|
-
len(conds) == 2
|
|
86
|
-
and conds["Available"].status == "True"
|
|
87
|
-
and conds["Progressing"].status == "True"
|
|
88
|
-
and conds["Progressing"].reason == "NewReplicaSetAvailable"
|
|
89
|
-
):
|
|
90
|
-
status = github.STATUSES.success
|
|
91
|
-
await github.update_deployment(
|
|
92
|
-
github_deployment_url,
|
|
93
|
-
status=status,
|
|
94
|
-
description="Deployed successfully",
|
|
95
|
-
environment_url=await get_ingress_url(api, namespace, app),
|
|
96
|
-
)
|
|
97
|
-
elif (
|
|
98
|
-
"Progressing" in conds
|
|
99
|
-
and conds["Progressing"].status == "False"
|
|
100
|
-
and conds["Progressing"].reason == "ProgressDeadlineExceeded"
|
|
101
|
-
):
|
|
102
|
-
status = github.STATUSES.failure
|
|
103
|
-
await github.update_deployment(
|
|
104
|
-
github_deployment_url,
|
|
105
|
-
status=status,
|
|
106
|
-
description="Failed to deploy. Check the pod or migrations.",
|
|
107
|
-
)
|
|
108
|
-
if status:
|
|
109
|
-
logger.info(f"Patching {deployment.metadata.name}.label.gitops/status to {status}")
|
|
110
|
-
patch = {"metadata": {"labels": {"gitops/status": status}}}
|
|
111
|
-
try:
|
|
112
|
-
await apps_api.patch_namespaced_deployment(
|
|
113
|
-
deployment.metadata.name, deployment.metadata.namespace, patch
|
|
114
|
-
)
|
|
115
|
-
except kubernetes_asyncio.client.exceptions.ApiException as e:
|
|
116
|
-
logger.warning(e, exc_info=True)
|
|
117
|
-
|
|
118
|
-
async def run(self):
|
|
119
|
-
logger.info("Starting deployment status watching loop")
|
|
120
|
-
logger.info("Loading kubernetes asyncio api")
|
|
121
|
-
while True:
|
|
122
|
-
try:
|
|
123
|
-
await self.load_config()
|
|
124
|
-
await self.process_work()
|
|
125
|
-
except Exception as e:
|
|
126
|
-
logger.error(str(e), exc_info=True)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|