gitops 1.1.2__tar.gz → 1.3.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.1.2 → gitops-1.3.0}/.release-please-manifest.json +1 -1
- {gitops-1.1.2 → gitops-1.3.0}/CHANGELOG.md +21 -0
- {gitops-1.1.2 → gitops-1.3.0}/PKG-INFO +1 -1
- {gitops-1.1.2 → gitops-1.3.0}/gitops/__init__.py +1 -1
- {gitops-1.1.2 → gitops-1.3.0}/gitops/core.py +4 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops/utils/apps.py +15 -1
- {gitops-1.1.2 → gitops-1.3.0}/gitops_server/main.py +1 -4
- gitops-1.3.0/gitops_server/version.py +1 -0
- gitops-1.3.0/gitops_server/workers/__init__.py +1 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops_server/workers/deployer/deploy.py +5 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops_server/workers/deployer/hooks.py +4 -1
- {gitops-1.1.2 → gitops-1.3.0}/pyproject.toml +1 -1
- {gitops-1.1.2 → gitops-1.3.0}/tests/sample_data.py +189 -0
- {gitops-1.1.2 → gitops-1.3.0}/tests/test_deploy.py +17 -1
- {gitops-1.1.2 → gitops-1.3.0}/tests/test_gitops_server.py +0 -11
- gitops-1.1.2/gitops_server/version.py +0 -1
- gitops-1.1.2/gitops_server/workers/__init__.py +0 -2
- gitops-1.1.2/gitops_server/workers/status_updater/__init__.py +0 -1
- gitops-1.1.2/gitops_server/workers/status_updater/worker.py +0 -126
- {gitops-1.1.2 → gitops-1.3.0}/.dockerignore +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/.github/workflows/release-please.yaml +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/.github/workflows/test.yml +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/.gitignore +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/.mise.toml +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/.pre-commit-config.yaml +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/Dockerfile +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/LICENSE +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/README.md +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/charts/gitops/.helmignore +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/charts/gitops/CHANGELOG.md +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/charts/gitops/Chart.yaml +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/charts/gitops/README.md +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/charts/gitops/templates/_helpers.tpl +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/charts/gitops/templates/certificate.yml +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/charts/gitops/templates/deployment.yml +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/charts/gitops/templates/environment.yml +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/charts/gitops/templates/ingress.yml +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/charts/gitops/templates/issuer.yml +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/charts/gitops/templates/secrets.yml +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/charts/gitops/templates/service.yml +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/charts/gitops/templates/serviceaccount.yaml +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/charts/gitops/values.yaml +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/docker-compose.yml +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops/common/__init__.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops/common/app.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops/common/utils.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops/main.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops/py.typed +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops/settings.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops/shorthands.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops/utils/__init__.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops/utils/async_runner.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops/utils/cli.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops/utils/exceptions.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops/utils/images.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops/utils/kube.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops/utils/tags.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops/utils/yaml.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops_server/CHANGELOG.md +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops_server/README.md +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops_server/__init__.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops_server/app.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops_server/settings.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops_server/types.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops_server/utils/__init__.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops_server/utils/git.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops_server/utils/github.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops_server/utils/slack.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops_server/workers/deployer/__init__.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/gitops_server/workers/deployer/worker.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/release-please-config.json +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/release.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/ruff.toml +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/secrets.example.env +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/setup.cfg +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/tests/__init__.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/tests/conftest.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/tests/test_app.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/tests/test_core.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/tests/test_git.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/tests/test_utils.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/tests/utils.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/tests/webhook_sample_data.py +0 -0
- {gitops-1.1.2 → gitops-1.3.0}/uv.lock +0 -0
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.3.0](https://github.com/uptick/gitops/compare/gitops-v1.2.0...gitops-v1.3.0) (2025-03-25)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **cli:** add skip deploy to gitops bump ([554b0a5](https://github.com/uptick/gitops/commit/554b0a5adf5dbea0f0ac3d020ac65aa1e3c831bd))
|
|
9
|
+
* **server:** skip deployment if --skip-deploy is present ([2cd9c16](https://github.com/uptick/gitops/commit/2cd9c16ab618d13660b5ef17079e39bbcd02d956))
|
|
10
|
+
|
|
11
|
+
## [1.2.0](https://github.com/uptick/gitops/compare/gitops-v1.1.2...gitops-v1.2.0) (2025-03-17)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
* **cli:** allow filtering apps via a csv file ([f465d9a](https://github.com/uptick/gitops/commit/f465d9a8ad565afde1f9bf7f9d1232281352882c))
|
|
17
|
+
* **cli:** allow filtering apps via a csv file ([7fa06cd](https://github.com/uptick/gitops/commit/7fa06cd6d8ca42a2d1e50304ad7cba7e688650e9))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
* remove status updater and replace with a post deploy hook ([e72c2ad](https://github.com/uptick/gitops/commit/e72c2ad2b46304f2258d7b3d1bb04e4200ce1602))
|
|
23
|
+
|
|
3
24
|
## [1.1.2](https://github.com/uptick/gitops/compare/gitops-v1.1.1...gitops-v1.1.2) (2025-02-14)
|
|
4
25
|
|
|
5
26
|
|
|
@@ -42,6 +42,7 @@ def bump( # noqa: C901
|
|
|
42
42
|
push=False,
|
|
43
43
|
redeploy=False,
|
|
44
44
|
skip_migrations=False,
|
|
45
|
+
skip_deploy=False,
|
|
45
46
|
):
|
|
46
47
|
"""Bump image tag on selected app(s).
|
|
47
48
|
Provide `image_tag` to set to a specific image tag, or provide `prefix` to use latest image
|
|
@@ -50,6 +51,7 @@ def bump( # noqa: C901
|
|
|
50
51
|
Provide `push` to automatically push the commit (and retry on conflict.)
|
|
51
52
|
Provide `redeploy` to redeploy servers even if nothing has changed.
|
|
52
53
|
Provide `skip_migrations` to disable running migrations via helm hooks.
|
|
54
|
+
Provide `skip_deploy` to skip deploying for non functional changes.
|
|
53
55
|
"""
|
|
54
56
|
prompt_message = "The following apps will have their image bumped"
|
|
55
57
|
if image_tag:
|
|
@@ -119,6 +121,8 @@ def bump( # noqa: C901
|
|
|
119
121
|
commit_message += f" to use prefix {prefix}"
|
|
120
122
|
if skip_migrations:
|
|
121
123
|
commit_message += " --skip-migrations"
|
|
124
|
+
if skip_deploy:
|
|
125
|
+
commit_message += " --skip-deploy"
|
|
122
126
|
|
|
123
127
|
run(f'cd {get_apps_directory()}; git commit -am "{commit_message}."')
|
|
124
128
|
|
|
@@ -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.3.0"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .deployer import DeployQueueWorker # NOQA
|
|
@@ -70,6 +70,7 @@ class Deployer:
|
|
|
70
70
|
self.failed_apps: set[str] = set()
|
|
71
71
|
|
|
72
72
|
self.post_ts: str | None = None
|
|
73
|
+
self.skip_deploy = "--skip-deploy" in commit_message
|
|
73
74
|
|
|
74
75
|
@classmethod
|
|
75
76
|
async def from_push_event(cls, push_event):
|
|
@@ -94,6 +95,10 @@ class Deployer:
|
|
|
94
95
|
)
|
|
95
96
|
|
|
96
97
|
async def deploy(self):
|
|
98
|
+
if self.skip_deploy:
|
|
99
|
+
logger.info("Skipping deploy due to `--skip-deploy` flag.")
|
|
100
|
+
return
|
|
101
|
+
|
|
97
102
|
self.added_apps, self.updated_apps, self.removed_apps = self.calculate_app_deltas()
|
|
98
103
|
current_span = trace.get_current_span()
|
|
99
104
|
if current_span:
|
|
@@ -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
|
|
|
@@ -375,3 +375,192 @@ SAMPLE_GITHUB_PAYLOAD_SKIP_MIGRATIONS = {
|
|
|
375
375
|
"site_admin": False,
|
|
376
376
|
},
|
|
377
377
|
}
|
|
378
|
+
|
|
379
|
+
SAMPLE_GITHUB_PAYLOAD_SKIP_DEPLOY = {
|
|
380
|
+
"ref": "refs/heads/master",
|
|
381
|
+
"before": "bef04e58a0001234567890123456789012345678",
|
|
382
|
+
"after": "af7e458a00012345678901234567890123456789",
|
|
383
|
+
"created": False,
|
|
384
|
+
"deleted": False,
|
|
385
|
+
"forced": False,
|
|
386
|
+
"base_ref": None,
|
|
387
|
+
"compare": "https://github.com/user/mock-repo/compare/bef04e58a000...af7e458a0001",
|
|
388
|
+
"commits": [
|
|
389
|
+
{
|
|
390
|
+
"id": "c099171d00c099171d00c099171d00c099171d00",
|
|
391
|
+
"tree_id": "74ee1d000074ee1d000074ee1d000074ee1d0000",
|
|
392
|
+
"distinct": True,
|
|
393
|
+
"message": "bump test-app-1 --skip-deploy",
|
|
394
|
+
"timestamp": "2019-07-18T15:07:37+10:00",
|
|
395
|
+
"url": ("https://github.com/user/mock-repo/commit/c099171d00c099171d00c099171d00c099171d00"),
|
|
396
|
+
"author": {
|
|
397
|
+
"name": "Author Fullname",
|
|
398
|
+
"email": "author@example.com",
|
|
399
|
+
"username": "authorusername",
|
|
400
|
+
},
|
|
401
|
+
"committer": {
|
|
402
|
+
"name": "Author Fullname",
|
|
403
|
+
"email": "author@example.com",
|
|
404
|
+
"username": "authorusername",
|
|
405
|
+
},
|
|
406
|
+
"added": [],
|
|
407
|
+
"removed": [],
|
|
408
|
+
"modified": ["apps/test-app-1/deployment.yml"],
|
|
409
|
+
}
|
|
410
|
+
],
|
|
411
|
+
"head_commit": {
|
|
412
|
+
"id": "c099171d00c099171d00c099171d00c099171d00",
|
|
413
|
+
"tree_id": "74ee1d000074ee1d000074ee1d000074ee1d0000",
|
|
414
|
+
"distinct": True,
|
|
415
|
+
"message": "bump test-app-1 --skip-deploy",
|
|
416
|
+
"timestamp": "2019-07-18T15:07:37+10:00",
|
|
417
|
+
"url": "https://github.com/user/mock-repo/commit/c099171d00c099171d00c099171d00c099171d00",
|
|
418
|
+
"author": {
|
|
419
|
+
"name": "Author Fullname",
|
|
420
|
+
"email": "author@example.com",
|
|
421
|
+
"username": "authorusername",
|
|
422
|
+
},
|
|
423
|
+
"committer": {
|
|
424
|
+
"name": "Author Fullname",
|
|
425
|
+
"email": "author@example.com",
|
|
426
|
+
"username": "authorusername",
|
|
427
|
+
},
|
|
428
|
+
"added": [],
|
|
429
|
+
"removed": [],
|
|
430
|
+
"modified": ["apps/test-app-1/deployment.yml"],
|
|
431
|
+
},
|
|
432
|
+
"repository": {
|
|
433
|
+
"id": 123123123,
|
|
434
|
+
"node_id": "NODEIDNODEIDNODEIDNODEIDNODEIDN=",
|
|
435
|
+
"name": "mock-repo",
|
|
436
|
+
"full_name": "user/mock-repo",
|
|
437
|
+
"private": True,
|
|
438
|
+
"owner": {
|
|
439
|
+
"name": "user",
|
|
440
|
+
"email": "admin@example.com",
|
|
441
|
+
"login": "user",
|
|
442
|
+
"id": 123123123,
|
|
443
|
+
"node_id": "NODEIDNODEIDNODEIDNODEIDNODEIDNO",
|
|
444
|
+
"avatar_url": "",
|
|
445
|
+
"gravatar_id": "",
|
|
446
|
+
"url": "https://api.github.com/users/user",
|
|
447
|
+
"html_url": "https://github.com/user",
|
|
448
|
+
"followers_url": "https://api.github.com/users/user/followers",
|
|
449
|
+
"following_url": "https://api.github.com/users/user/following{/other_user}",
|
|
450
|
+
"gists_url": "https://api.github.com/users/user/gists{/gist_id}",
|
|
451
|
+
"starred_url": "https://api.github.com/users/user/starred{/owner}{/repo}",
|
|
452
|
+
"subscriptions_url": "https://api.github.com/users/user/subscriptions",
|
|
453
|
+
"organizations_url": "https://api.github.com/users/user/orgs",
|
|
454
|
+
"repos_url": "https://api.github.com/users/user/repos",
|
|
455
|
+
"events_url": "https://api.github.com/users/user/events{/privacy}",
|
|
456
|
+
"received_events_url": "https://api.github.com/users/user/received_events",
|
|
457
|
+
"type": "Organization",
|
|
458
|
+
"site_admin": False,
|
|
459
|
+
},
|
|
460
|
+
"html_url": "https://github.com/user/mock-repo",
|
|
461
|
+
"description": "Kubernetes cluster configuration.",
|
|
462
|
+
"fork": False,
|
|
463
|
+
"url": "https://github.com/user/mock-repo",
|
|
464
|
+
"forks_url": "https://api.github.com/repos/user/mock-repo/forks",
|
|
465
|
+
"keys_url": "https://api.github.com/repos/user/mock-repo/keys{/key_id}",
|
|
466
|
+
"collaborators_url": ("https://api.github.com/repos/user/mock-repo/collaborators{/collaborator}"),
|
|
467
|
+
"teams_url": "https://api.github.com/repos/user/mock-repo/teams",
|
|
468
|
+
"hooks_url": "https://api.github.com/repos/user/mock-repo/hooks",
|
|
469
|
+
"issue_events_url": "https://api.github.com/repos/user/mock-repo/issues/events{/number}",
|
|
470
|
+
"events_url": "https://api.github.com/repos/user/mock-repo/events",
|
|
471
|
+
"assignees_url": "https://api.github.com/repos/user/mock-repo/assignees{/user}",
|
|
472
|
+
"branches_url": "https://api.github.com/repos/user/mock-repo/branches{/branch}",
|
|
473
|
+
"tags_url": "https://api.github.com/repos/user/mock-repo/tags",
|
|
474
|
+
"blobs_url": "https://api.github.com/repos/user/mock-repo/git/blobs{/sha}",
|
|
475
|
+
"git_tags_url": "https://api.github.com/repos/user/mock-repo/git/tags{/sha}",
|
|
476
|
+
"git_refs_url": "https://api.github.com/repos/user/mock-repo/git/refs{/sha}",
|
|
477
|
+
"trees_url": "https://api.github.com/repos/user/mock-repo/git/trees{/sha}",
|
|
478
|
+
"statuses_url": "https://api.github.com/repos/user/mock-repo/statuses/{sha}",
|
|
479
|
+
"languages_url": "https://api.github.com/repos/user/mock-repo/languages",
|
|
480
|
+
"stargazers_url": "https://api.github.com/repos/user/mock-repo/stargazers",
|
|
481
|
+
"contributors_url": "https://api.github.com/repos/user/mock-repo/contributors",
|
|
482
|
+
"subscribers_url": "https://api.github.com/repos/user/mock-repo/subscribers",
|
|
483
|
+
"subscription_url": "https://api.github.com/repos/user/mock-repo/subscription",
|
|
484
|
+
"commits_url": "https://api.github.com/repos/user/mock-repo/commits{/sha}",
|
|
485
|
+
"git_commits_url": "https://api.github.com/repos/user/mock-repo/git/commits{/sha}",
|
|
486
|
+
"comments_url": "https://api.github.com/repos/user/mock-repo/comments{/number}",
|
|
487
|
+
"issue_comment_url": "https://api.github.com/repos/user/mock-repo/issues/comments{/number}",
|
|
488
|
+
"contents_url": "https://api.github.com/repos/user/mock-repo/contents/{+path}",
|
|
489
|
+
"compare_url": "https://api.github.com/repos/user/mock-repo/compare/{base}...{head}",
|
|
490
|
+
"merges_url": "https://api.github.com/repos/user/mock-repo/merges",
|
|
491
|
+
"archive_url": "https://api.github.com/repos/user/mock-repo/{archive_format}{/ref}",
|
|
492
|
+
"downloads_url": "https://api.github.com/repos/user/mock-repo/downloads",
|
|
493
|
+
"issues_url": "https://api.github.com/repos/user/mock-repo/issues{/number}",
|
|
494
|
+
"pulls_url": "https://api.github.com/repos/user/mock-repo/pulls{/number}",
|
|
495
|
+
"milestones_url": "https://api.github.com/repos/user/mock-repo/milestones{/number}",
|
|
496
|
+
"notifications_url": ("https://api.github.com/repos/user/mock-repo/notifications{?since,all,participating}"),
|
|
497
|
+
"labels_url": "https://api.github.com/repos/user/mock-repo/labels{/name}",
|
|
498
|
+
"releases_url": "https://api.github.com/repos/user/mock-repo/releases{/id}",
|
|
499
|
+
"deployments_url": "https://api.github.com/repos/user/mock-repo/deployments",
|
|
500
|
+
"created_at": 123123,
|
|
501
|
+
"updated_at": "2019-07-18T05:05:03Z",
|
|
502
|
+
"pushed_at": 123123,
|
|
503
|
+
"git_url": "git://github.com/user/mock-repo.git",
|
|
504
|
+
"ssh_url": "git@github.com:user/mock-repo.git",
|
|
505
|
+
"clone_url": "https://github.com/user/mock-repo.git",
|
|
506
|
+
"svn_url": "https://github.com/user/mock-repo",
|
|
507
|
+
"homepage": "",
|
|
508
|
+
"size": 2917,
|
|
509
|
+
"stargazers_count": 0,
|
|
510
|
+
"watchers_count": 0,
|
|
511
|
+
"language": "Python",
|
|
512
|
+
"has_issues": True,
|
|
513
|
+
"has_projects": True,
|
|
514
|
+
"has_downloads": True,
|
|
515
|
+
"has_wiki": True,
|
|
516
|
+
"has_pages": False,
|
|
517
|
+
"forks_count": 0,
|
|
518
|
+
"mirror_url": None,
|
|
519
|
+
"archived": False,
|
|
520
|
+
"disabled": False,
|
|
521
|
+
"open_issues_count": 5,
|
|
522
|
+
"license": None,
|
|
523
|
+
"forks": 0,
|
|
524
|
+
"open_issues": 5,
|
|
525
|
+
"watchers": 0,
|
|
526
|
+
"default_branch": "master",
|
|
527
|
+
"stargazers": 0,
|
|
528
|
+
"master_branch": "master",
|
|
529
|
+
"organization": "user",
|
|
530
|
+
},
|
|
531
|
+
"pusher": {"name": "authorusername", "email": "author@example.com"},
|
|
532
|
+
"organization": {
|
|
533
|
+
"login": "user",
|
|
534
|
+
"id": 123123,
|
|
535
|
+
"node_id": "NODEID00NODEID00NODEID00NODEID00",
|
|
536
|
+
"url": "https://api.github.com/orgs/user",
|
|
537
|
+
"repos_url": "https://api.github.com/orgs/user/repos",
|
|
538
|
+
"events_url": "https://api.github.com/orgs/user/events",
|
|
539
|
+
"hooks_url": "https://api.github.com/orgs/user/hooks",
|
|
540
|
+
"issues_url": "https://api.github.com/orgs/user/issues",
|
|
541
|
+
"members_url": "https://api.github.com/orgs/user/members{/member}",
|
|
542
|
+
"public_members_url": "https://api.github.com/orgs/user/public_members{/member}",
|
|
543
|
+
"avatar_url": "",
|
|
544
|
+
"description": "",
|
|
545
|
+
},
|
|
546
|
+
"sender": {
|
|
547
|
+
"login": "authorusername",
|
|
548
|
+
"id": 123123,
|
|
549
|
+
"node_id": "NODEID00NODEID00NOD=",
|
|
550
|
+
"avatar_url": "https://avatars0.githubusercontent.com/u/5036488?v=4",
|
|
551
|
+
"gravatar_id": "",
|
|
552
|
+
"url": "https://api.github.com/users/authorusername",
|
|
553
|
+
"html_url": "https://github.com/authorusername",
|
|
554
|
+
"followers_url": "https://api.github.com/users/authorusername/followers",
|
|
555
|
+
"following_url": "https://api.github.com/users/authorusername/following{/other_user}",
|
|
556
|
+
"gists_url": "https://api.github.com/users/authorusername/gists{/gist_id}",
|
|
557
|
+
"starred_url": "https://api.github.com/users/authorusername/starred{/owner}{/repo}",
|
|
558
|
+
"subscriptions_url": "https://api.github.com/users/authorusername/subscriptions",
|
|
559
|
+
"organizations_url": "https://api.github.com/users/authorusername/orgs",
|
|
560
|
+
"repos_url": "https://api.github.com/users/authorusername/repos",
|
|
561
|
+
"events_url": "https://api.github.com/users/authorusername/events{/privacy}",
|
|
562
|
+
"received_events_url": "https://api.github.com/users/authorusername/received_events",
|
|
563
|
+
"type": "User",
|
|
564
|
+
"site_admin": False,
|
|
565
|
+
},
|
|
566
|
+
}
|
|
@@ -7,7 +7,7 @@ from gitops.common.app import App
|
|
|
7
7
|
from gitops_server.types import AppDefinitions
|
|
8
8
|
from gitops_server.workers.deployer import Deployer
|
|
9
9
|
|
|
10
|
-
from .sample_data import SAMPLE_GITHUB_PAYLOAD, SAMPLE_GITHUB_PAYLOAD_SKIP_MIGRATIONS
|
|
10
|
+
from .sample_data import SAMPLE_GITHUB_PAYLOAD, SAMPLE_GITHUB_PAYLOAD_SKIP_DEPLOY, SAMPLE_GITHUB_PAYLOAD_SKIP_MIGRATIONS
|
|
11
11
|
from .utils import create_test_yaml, mock_load_app_definitions
|
|
12
12
|
|
|
13
13
|
# Patch gitops_server.git.run & check correct commands + order
|
|
@@ -102,6 +102,22 @@ class TestDeploy:
|
|
|
102
102
|
)
|
|
103
103
|
assert post_mock.call_count == 2
|
|
104
104
|
|
|
105
|
+
@patch("gitops_server.workers.deployer.deploy.run")
|
|
106
|
+
@patch("gitops_server.utils.slack.post")
|
|
107
|
+
@patch("gitops_server.workers.deployer.deploy.load_app_definitions", mock_load_app_definitions)
|
|
108
|
+
@patch("gitops_server.workers.deployer.deploy.temp_repo")
|
|
109
|
+
async def test_skip_deploy_in_commit_message_should_not_run_deployment(self, temp_repo_mock, post_mock, run_mock):
|
|
110
|
+
"""Fake a deploy to two servers, bumping fg from 2 to 4."""
|
|
111
|
+
run_mock.return_value = {"exit_code": 0, "output": ""}
|
|
112
|
+
temp_repo_mock.return_value.__aenter__.return_value = "mock-repo"
|
|
113
|
+
deployer = await Deployer.from_push_event(SAMPLE_GITHUB_PAYLOAD_SKIP_DEPLOY)
|
|
114
|
+
|
|
115
|
+
assert deployer.skip_deploy is True
|
|
116
|
+
await deployer.deploy()
|
|
117
|
+
|
|
118
|
+
assert run_mock.call_count == 0
|
|
119
|
+
assert post_mock.call_count == 0
|
|
120
|
+
|
|
105
121
|
|
|
106
122
|
class TestLoadAppDefinitions:
|
|
107
123
|
def test_load_app_definitions_ignores_suspended_apps(self):
|
|
@@ -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.2"
|
|
@@ -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
|