kubex-core 0.1.0a1__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.
- kubex_core-0.1.0a1/.gitignore +171 -0
- kubex_core-0.1.0a1/PKG-INFO +34 -0
- kubex_core-0.1.0a1/README.md +14 -0
- kubex_core-0.1.0a1/kubex_core/__init__.py +0 -0
- kubex_core-0.1.0a1/kubex_core/models/__init__.py +0 -0
- kubex_core-0.1.0a1/kubex_core/models/base.py +8 -0
- kubex_core-0.1.0a1/kubex_core/models/base_entity.py +15 -0
- kubex_core-0.1.0a1/kubex_core/models/interfaces.py +19 -0
- kubex_core-0.1.0a1/kubex_core/models/list_entity.py +14 -0
- kubex_core-0.1.0a1/kubex_core/models/metadata.py +45 -0
- kubex_core-0.1.0a1/kubex_core/models/partial_object_meta.py +14 -0
- kubex_core-0.1.0a1/kubex_core/models/resource_config.py +158 -0
- kubex_core-0.1.0a1/kubex_core/models/scale.py +22 -0
- kubex_core-0.1.0a1/kubex_core/models/status.py +60 -0
- kubex_core-0.1.0a1/kubex_core/models/typing.py +9 -0
- kubex_core-0.1.0a1/kubex_core/models/watch_event.py +36 -0
- kubex_core-0.1.0a1/kubex_core/py.typed +0 -0
- kubex_core-0.1.0a1/pyproject.toml +35 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py,cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
#Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# poetry
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
102
|
+
#poetry.lock
|
|
103
|
+
|
|
104
|
+
# pdm
|
|
105
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
106
|
+
#pdm.lock
|
|
107
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
108
|
+
# in version control.
|
|
109
|
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
|
110
|
+
.pdm.toml
|
|
111
|
+
.pdm-python
|
|
112
|
+
.pdm-build/
|
|
113
|
+
|
|
114
|
+
uv.lock
|
|
115
|
+
.python-version
|
|
116
|
+
|
|
117
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
118
|
+
__pypackages__/
|
|
119
|
+
|
|
120
|
+
# Celery stuff
|
|
121
|
+
celerybeat-schedule
|
|
122
|
+
celerybeat.pid
|
|
123
|
+
|
|
124
|
+
# SageMath parsed files
|
|
125
|
+
*.sage.py
|
|
126
|
+
|
|
127
|
+
# Environments
|
|
128
|
+
.env
|
|
129
|
+
.venv
|
|
130
|
+
env/
|
|
131
|
+
venv/
|
|
132
|
+
ENV/
|
|
133
|
+
env.bak/
|
|
134
|
+
venv.bak/
|
|
135
|
+
|
|
136
|
+
# Spyder project settings
|
|
137
|
+
.spyderproject
|
|
138
|
+
.spyproject
|
|
139
|
+
|
|
140
|
+
# Rope project settings
|
|
141
|
+
.ropeproject
|
|
142
|
+
|
|
143
|
+
# mkdocs documentation
|
|
144
|
+
/site
|
|
145
|
+
|
|
146
|
+
# mypy
|
|
147
|
+
.mypy_cache/
|
|
148
|
+
.dmypy.json
|
|
149
|
+
dmypy.json
|
|
150
|
+
|
|
151
|
+
# Pyre type checker
|
|
152
|
+
.pyre/
|
|
153
|
+
|
|
154
|
+
# pytype static type analyzer
|
|
155
|
+
.pytype/
|
|
156
|
+
|
|
157
|
+
# Cython debug symbols
|
|
158
|
+
cython_debug/
|
|
159
|
+
|
|
160
|
+
# PyCharm
|
|
161
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
162
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
163
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
164
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
165
|
+
#.idea/
|
|
166
|
+
*.code-workspace
|
|
167
|
+
.vscode/
|
|
168
|
+
|
|
169
|
+
# Ruff
|
|
170
|
+
.ruff_cache/
|
|
171
|
+
scratches/
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kubex-core
|
|
3
|
+
Version: 0.1.0a1
|
|
4
|
+
Summary: Core model types shared by Kubex and its generated resource packages
|
|
5
|
+
Author-email: Codemageddon <dev@codemageddon.me>
|
|
6
|
+
License: MIT License
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Requires-Dist: pydantic<3,>=2.0
|
|
18
|
+
Requires-Dist: pyyaml>=6.0.2
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# kubex-core
|
|
22
|
+
|
|
23
|
+
Shared foundation types for [Kubex](https://github.com/codemageddon/kubex) and its generated Kubernetes resource packages (`kubex-k8s-*`).
|
|
24
|
+
|
|
25
|
+
Contains:
|
|
26
|
+
|
|
27
|
+
- `kubex_core.models.base.BaseK8sModel` — Pydantic base with camelCase aliasing.
|
|
28
|
+
- `kubex_core.models.base_entity.BaseEntity` — base class for all K8s resources.
|
|
29
|
+
- `kubex_core.models.interfaces` — marker interfaces (`NamespaceScopedEntity`, `ClusterScopedEntity`, `HasLogs`, `HasStatusSubresource`, `HasScaleSubresource`, `Evictable`).
|
|
30
|
+
- `kubex_core.models.resource_config` — `ResourceConfig` descriptor + `Scope`.
|
|
31
|
+
- `kubex_core.models.metadata` — `ObjectMetadata`, `ListMetadata`, `OwnerReference`.
|
|
32
|
+
- `kubex_core.models.watch_event`, `list_entity`, `partial_object_meta`, `status`, `scale`.
|
|
33
|
+
|
|
34
|
+
This package is a runtime dependency of the `kubex` client and every generated resource package. End users generally install `kubex` + one `kubex-k8s-*` of choice; this package is pulled in transitively.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# kubex-core
|
|
2
|
+
|
|
3
|
+
Shared foundation types for [Kubex](https://github.com/codemageddon/kubex) and its generated Kubernetes resource packages (`kubex-k8s-*`).
|
|
4
|
+
|
|
5
|
+
Contains:
|
|
6
|
+
|
|
7
|
+
- `kubex_core.models.base.BaseK8sModel` — Pydantic base with camelCase aliasing.
|
|
8
|
+
- `kubex_core.models.base_entity.BaseEntity` — base class for all K8s resources.
|
|
9
|
+
- `kubex_core.models.interfaces` — marker interfaces (`NamespaceScopedEntity`, `ClusterScopedEntity`, `HasLogs`, `HasStatusSubresource`, `HasScaleSubresource`, `Evictable`).
|
|
10
|
+
- `kubex_core.models.resource_config` — `ResourceConfig` descriptor + `Scope`.
|
|
11
|
+
- `kubex_core.models.metadata` — `ObjectMetadata`, `ListMetadata`, `OwnerReference`.
|
|
12
|
+
- `kubex_core.models.watch_event`, `list_entity`, `partial_object_meta`, `status`, `scale`.
|
|
13
|
+
|
|
14
|
+
This package is a runtime dependency of the `kubex` client and every generated resource package. End users generally install `kubex` + one `kubex-k8s-*` of choice; this package is pulled in transitively.
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from typing import ClassVar, Self
|
|
2
|
+
|
|
3
|
+
from kubex_core.models.base import BaseK8sModel
|
|
4
|
+
from kubex_core.models.metadata import ObjectMetadata
|
|
5
|
+
from kubex_core.models.resource_config import ResourceConfig
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BaseEntity(BaseK8sModel):
|
|
9
|
+
"""BaseEntity is the common fields for all entities."""
|
|
10
|
+
|
|
11
|
+
__RESOURCE_CONFIG__: ClassVar[ResourceConfig[Self]] = ResourceConfig()
|
|
12
|
+
|
|
13
|
+
api_version: str | None = None
|
|
14
|
+
kind: str | None = None
|
|
15
|
+
metadata: ObjectMetadata
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from kubex_core.models.base_entity import BaseEntity
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ClusterScopedEntity(BaseEntity): ...
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class NamespaceScopedEntity(BaseEntity): ...
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class HasStatusSubresource(BaseEntity): ...
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class HasScaleSubresource(BaseEntity): ...
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class HasLogs(BaseEntity): ...
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Evictable(BaseEntity): ...
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from typing import Generic
|
|
2
|
+
|
|
3
|
+
from kubex_core.models.base import BaseK8sModel
|
|
4
|
+
from kubex_core.models.metadata import ListMetadata
|
|
5
|
+
from kubex_core.models.typing import ResourceType
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ListEntity(BaseK8sModel, Generic[ResourceType]):
|
|
9
|
+
"""ListEntity is the common fields for all list entities."""
|
|
10
|
+
|
|
11
|
+
api_version: str
|
|
12
|
+
kind: str
|
|
13
|
+
metadata: ListMetadata
|
|
14
|
+
items: list[ResourceType]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import datetime
|
|
4
|
+
|
|
5
|
+
from pydantic import Field
|
|
6
|
+
|
|
7
|
+
from kubex_core.models.base import BaseK8sModel
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class OwnerReference(BaseK8sModel):
|
|
11
|
+
"""OwnerReference contains enough information to let you identify an owning object."""
|
|
12
|
+
|
|
13
|
+
api_version: str
|
|
14
|
+
kind: str
|
|
15
|
+
name: str
|
|
16
|
+
uid: str
|
|
17
|
+
controller: bool | None = None
|
|
18
|
+
block_owner_deletion: bool | None = None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ObjectMetadata(BaseK8sModel):
|
|
22
|
+
"""CommonMetadata is the common metadata for all Kubernetes API objects."""
|
|
23
|
+
|
|
24
|
+
labels: dict[str, str] | None = None
|
|
25
|
+
annotations: dict[str, str] | None = None
|
|
26
|
+
finalizers: list[str] | None = None
|
|
27
|
+
creation_timestamp: datetime.datetime | None = None
|
|
28
|
+
deletion_timestamp: datetime.datetime | None = None
|
|
29
|
+
deletion_grace_period_seconds: int | None = None
|
|
30
|
+
generation: int | None = None
|
|
31
|
+
resource_version: str | None = None
|
|
32
|
+
uid: str | None = None
|
|
33
|
+
name: str | None = None
|
|
34
|
+
namespace: str | None = None
|
|
35
|
+
generate_name: str | None = None
|
|
36
|
+
owner_references: list[OwnerReference] | None = None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ListMetadata(BaseK8sModel):
|
|
40
|
+
"""ListMeta describes metadata that synthetic resources must have, including lists and various status objects."""
|
|
41
|
+
|
|
42
|
+
continue_: str | None = Field(None, alias="continue")
|
|
43
|
+
remaining_item_count: int | None = None
|
|
44
|
+
resource_version: str | None = None
|
|
45
|
+
self_link: str | None = None
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
from kubex_core.models.base_entity import BaseEntity
|
|
6
|
+
from kubex_core.models.metadata import ObjectMetadata
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PartialObjectMetadata(BaseEntity):
|
|
10
|
+
"""PartialObjectMetadata is the common metadata for all Kubernetes API objects."""
|
|
11
|
+
|
|
12
|
+
api_version: Literal["meta.k8s.io/v1"] = "meta.k8s.io/v1"
|
|
13
|
+
kind: Literal["PartialObjectMetadata"] = "PartialObjectMetadata"
|
|
14
|
+
metadata: ObjectMetadata
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from functools import cached_property
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Generic, Literal, Self, Type, cast
|
|
6
|
+
|
|
7
|
+
from pydantic import create_model
|
|
8
|
+
|
|
9
|
+
from kubex_core.models.typing import ResourceType
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from kubex_core.models.list_entity import ListEntity
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Scope(Enum):
|
|
16
|
+
CLUSTER = "cluster"
|
|
17
|
+
NAMESPACE = "namespace"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ResourceConfig(Generic[ResourceType]):
|
|
21
|
+
"""ResourceConfig is the configuration for a resource."""
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
version: str | None = None,
|
|
26
|
+
kind: str | None = None,
|
|
27
|
+
plural: str | None = None,
|
|
28
|
+
scope: Scope | None = None,
|
|
29
|
+
group: str | None = None,
|
|
30
|
+
list_model: Type[ListEntity[ResourceType]] | None = None,
|
|
31
|
+
) -> None:
|
|
32
|
+
self._version = version
|
|
33
|
+
self._kind = kind
|
|
34
|
+
self._plural = plural
|
|
35
|
+
self._scope = scope
|
|
36
|
+
self._group = group
|
|
37
|
+
self._list_model = list_model
|
|
38
|
+
|
|
39
|
+
def __get__(self, instance: Any, owner: Type[ResourceType]) -> Self:
|
|
40
|
+
"""Fill in the missing values from the owner."""
|
|
41
|
+
if kind_field := owner.model_fields.get("kind"):
|
|
42
|
+
if kind_field.default is not None:
|
|
43
|
+
self._kind = kind_field.default
|
|
44
|
+
else:
|
|
45
|
+
return self
|
|
46
|
+
if self._version is None or self._group is None:
|
|
47
|
+
if api_version_field := owner.model_fields.get("api_version"):
|
|
48
|
+
if api_version_field.default is not None:
|
|
49
|
+
self._version, self._group = get_version_and_froup_from_api_version(
|
|
50
|
+
api_version_field.default
|
|
51
|
+
)
|
|
52
|
+
else:
|
|
53
|
+
return self
|
|
54
|
+
if self._list_model is None:
|
|
55
|
+
self._list_model = create_list_model(owner, self)
|
|
56
|
+
if self._scope is None:
|
|
57
|
+
self._scope = Scope.NAMESPACE
|
|
58
|
+
if self._plural is None:
|
|
59
|
+
if self._kind is None:
|
|
60
|
+
raise ValueError("kind is not set")
|
|
61
|
+
if self._kind.endswith("y"):
|
|
62
|
+
self._plural = f"{self._kind[:-1].lower()}ies"
|
|
63
|
+
elif self._kind.endswith("s") or self._kind.endswith("x"):
|
|
64
|
+
self._plural = f"{self._kind.lower()}es"
|
|
65
|
+
else:
|
|
66
|
+
self._plural = f"{self._kind.lower()}s"
|
|
67
|
+
return self
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def version(self) -> str:
|
|
71
|
+
if self._version is None:
|
|
72
|
+
raise ValueError("version is not set")
|
|
73
|
+
return self._version
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def kind(self) -> str:
|
|
77
|
+
if self._kind is None:
|
|
78
|
+
raise ValueError("kind is not set")
|
|
79
|
+
return self._kind
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def plural(self) -> str:
|
|
83
|
+
if self._plural is None:
|
|
84
|
+
raise ValueError("plural is not set")
|
|
85
|
+
return self._plural
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def scope(self) -> Scope:
|
|
89
|
+
if self._scope is None:
|
|
90
|
+
raise ValueError("scope is not set")
|
|
91
|
+
return self._scope
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def group(self) -> str:
|
|
95
|
+
if self._group is None:
|
|
96
|
+
raise ValueError("group is not set")
|
|
97
|
+
return self._group
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def list_model(self) -> Type[ListEntity[ResourceType]]:
|
|
101
|
+
if self._list_model is None:
|
|
102
|
+
raise ValueError("list_model is not set")
|
|
103
|
+
return self._list_model
|
|
104
|
+
|
|
105
|
+
def url(self, namespace: str | None = None, name: str | None = None) -> str:
|
|
106
|
+
"""url returns the URL for the resource."""
|
|
107
|
+
if self.group and self.group != "core":
|
|
108
|
+
api_version = f"/apis/{self.group}/{self.version}"
|
|
109
|
+
else:
|
|
110
|
+
api_version = f"/api/{self.version}"
|
|
111
|
+
|
|
112
|
+
url: str
|
|
113
|
+
if namespace is None:
|
|
114
|
+
url = f"{api_version}/{self.plural}"
|
|
115
|
+
elif self.scope == Scope.CLUSTER:
|
|
116
|
+
raise ValueError("resource is cluster-scoped but namespace is set")
|
|
117
|
+
else:
|
|
118
|
+
url = f"{api_version}/namespaces/{namespace}/{self.plural}"
|
|
119
|
+
|
|
120
|
+
if name is None:
|
|
121
|
+
return url
|
|
122
|
+
return f"{url}/{name}"
|
|
123
|
+
|
|
124
|
+
@cached_property
|
|
125
|
+
def api_version(self) -> str:
|
|
126
|
+
if self.group and self.group != "core":
|
|
127
|
+
return f"{self.group}/{self.version}"
|
|
128
|
+
return self.version
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def create_list_model(
|
|
132
|
+
single_model: Type[ResourceType], resource_config: ResourceConfig[ResourceType]
|
|
133
|
+
) -> Type[ListEntity[ResourceType]]:
|
|
134
|
+
from kubex_core.models.list_entity import ListEntity
|
|
135
|
+
from kubex_core.models.metadata import ListMetadata
|
|
136
|
+
|
|
137
|
+
kind = f"{resource_config.kind}List"
|
|
138
|
+
list_model = create_model(
|
|
139
|
+
kind,
|
|
140
|
+
api_version=(Literal[resource_config.api_version], resource_config.api_version),
|
|
141
|
+
kind=(Literal[kind], kind),
|
|
142
|
+
metadata=(ListMetadata, ...),
|
|
143
|
+
items=(list[single_model], ...), # type: ignore[valid-type]
|
|
144
|
+
__base__=ListEntity[single_model], # type: ignore[valid-type]
|
|
145
|
+
)
|
|
146
|
+
# pydantic.create_model's return type is `type[BaseModel]`; we know the
|
|
147
|
+
# __base__ narrows it to ListEntity[ResourceType], but mypy cannot prove it.
|
|
148
|
+
return cast("Type[ListEntity[ResourceType]]", list_model)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def get_version_and_froup_from_api_version(api_version: str | None) -> tuple[str, str]:
|
|
152
|
+
"""get_version_and_group_from_api_version returns the version and group from the apiVersion."""
|
|
153
|
+
if api_version is None:
|
|
154
|
+
raise ValueError("api_version is not set")
|
|
155
|
+
parts = api_version.split("/")
|
|
156
|
+
if len(parts) == 1:
|
|
157
|
+
return parts[0], "core"
|
|
158
|
+
return parts[1], parts[0]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from .base import BaseK8sModel
|
|
4
|
+
from .base_entity import BaseEntity
|
|
5
|
+
from .metadata import ObjectMetadata
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ScaleSpec(BaseK8sModel):
|
|
9
|
+
replicas: int
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ScaleStatus(BaseK8sModel):
|
|
13
|
+
replicas: int
|
|
14
|
+
selector: str | None = None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Scale(BaseEntity):
|
|
18
|
+
api_version: Literal["autoscaling/v1"] = "autoscaling/v1"
|
|
19
|
+
kind: Literal["Scale"] = "Scale"
|
|
20
|
+
metadata: ObjectMetadata
|
|
21
|
+
spec: ScaleSpec
|
|
22
|
+
status: dict[str, int] | None = None
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from .base import BaseK8sModel
|
|
4
|
+
from .base_entity import BaseEntity
|
|
5
|
+
from .resource_config import ResourceConfig, Scope
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class StatusCause(BaseK8sModel):
|
|
9
|
+
"""StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered."""
|
|
10
|
+
|
|
11
|
+
message: str
|
|
12
|
+
"""A human-readable description of the cause of the error. This field may be presented as-is to a reader."""
|
|
13
|
+
field: str | None = None
|
|
14
|
+
"""The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional. Examples: "name" - the field "name" on the current resource "items[0].name" - the field "name" on the first array entry in \"items\"
|
|
15
|
+
"""
|
|
16
|
+
reason: str | None = None
|
|
17
|
+
"""A machine-readable description of the cause of the error. If this value is empty there is no information available."""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class StatusDetails(BaseK8sModel):
|
|
21
|
+
"""tatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined."""
|
|
22
|
+
|
|
23
|
+
causes: list[StatusCause] | None = None
|
|
24
|
+
"""The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes."""
|
|
25
|
+
name: str | None = None
|
|
26
|
+
"""The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described)."""
|
|
27
|
+
group: str | None = None
|
|
28
|
+
"""The group attribute of the resource associated with the status StatusReason."""
|
|
29
|
+
kind: str | None = None
|
|
30
|
+
"""The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds"""
|
|
31
|
+
retry_after_seconds: int | None = None
|
|
32
|
+
"""If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action."""
|
|
33
|
+
uid: str | None = None
|
|
34
|
+
"""UID of the resource. (when there is a single resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids"""
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Status(BaseEntity):
|
|
38
|
+
"""Status is a return value for calls that don't return other objects."""
|
|
39
|
+
|
|
40
|
+
api_version: Literal["meta/v1"] | None = "meta/v1"
|
|
41
|
+
"""APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources"""
|
|
42
|
+
kind: Literal["Status"] | None = "Status"
|
|
43
|
+
"""Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds"""
|
|
44
|
+
code: int = 0
|
|
45
|
+
"""Suggested HTTP return code for this status, 0 if not set."""
|
|
46
|
+
details: StatusDetails | None = None
|
|
47
|
+
"""Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type."""
|
|
48
|
+
message: str | None = None
|
|
49
|
+
"""A human-readable description of the status of this operation."""
|
|
50
|
+
reason: str | None = None
|
|
51
|
+
"""A machine-readable description of why this operation is in the "Failure" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it."""
|
|
52
|
+
status: Literal["Success", "Failure"]
|
|
53
|
+
"""Status of the operation. One of: "Success" or "Failure". More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status"""
|
|
54
|
+
|
|
55
|
+
__RESOURCE_CONFIG__ = ResourceConfig["Status"](
|
|
56
|
+
version="meta/v1",
|
|
57
|
+
kind="Status",
|
|
58
|
+
plural="statuses",
|
|
59
|
+
scope=Scope.CLUSTER,
|
|
60
|
+
)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import Any, Generic, Type
|
|
3
|
+
|
|
4
|
+
from .base_entity import BaseEntity
|
|
5
|
+
from .typing import ResourceType
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class EventType(str, Enum):
|
|
9
|
+
"""EventType is the type of the watch event."""
|
|
10
|
+
|
|
11
|
+
ADDED = "ADDED"
|
|
12
|
+
MODIFIED = "MODIFIED"
|
|
13
|
+
DELETED = "DELETED"
|
|
14
|
+
BOOKMARK = "BOOKMARK"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Bookmark(BaseEntity):
|
|
18
|
+
"""Bookmark is a pointer to a resource in a stream."""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class WatchEvent(Generic[ResourceType]):
|
|
22
|
+
"""WatchEvent represents a single event from a watch stream."""
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self, resource_type: Type[ResourceType], raw_event: dict[str, Any]
|
|
26
|
+
) -> None:
|
|
27
|
+
self._resource_type = resource_type
|
|
28
|
+
self.type = EventType(raw_event["type"])
|
|
29
|
+
self.object: ResourceType | Bookmark
|
|
30
|
+
if self.type == EventType.BOOKMARK:
|
|
31
|
+
self.object = Bookmark.model_validate(raw_event["object"])
|
|
32
|
+
else:
|
|
33
|
+
self.object = self._resource_type.model_validate(raw_event["object"])
|
|
34
|
+
|
|
35
|
+
def __repr__(self) -> str:
|
|
36
|
+
return f"WatchEvent(type={self.type}, object={self.object})"
|
|
File without changes
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "kubex-core"
|
|
3
|
+
version = "0.1.0-alpha.1"
|
|
4
|
+
description = "Core model types shared by Kubex and its generated resource packages"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
license = {text = "MIT License"}
|
|
8
|
+
authors = [
|
|
9
|
+
{name = "Codemageddon", email = "dev@codemageddon.me"},
|
|
10
|
+
]
|
|
11
|
+
dependencies = [
|
|
12
|
+
"pydantic>=2.0,<3",
|
|
13
|
+
"pyyaml>=6.0.2",
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Programming Language :: Python :: 3.14",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[build-system]
|
|
28
|
+
requires = ["hatchling"]
|
|
29
|
+
build-backend = "hatchling.build"
|
|
30
|
+
|
|
31
|
+
[tool.hatch.build.targets.sdist]
|
|
32
|
+
only-include = ["kubex_core"]
|
|
33
|
+
|
|
34
|
+
[tool.hatch.build.targets.wheel]
|
|
35
|
+
packages = ["kubex_core"]
|