Skip to content

Commit

Permalink
Create and Delete objects from files (#166)
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobtomlinson authored Sep 24, 2023
1 parent 2e5e5cb commit 842f760
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 8 deletions.
50 changes: 50 additions & 0 deletions examples/kubectl-ng/kubectl_ng/_create.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# SPDX-FileCopyrightText: Copyright (c) 2023, Dask Developers, NVIDIA
# SPDX-License-Identifier: BSD 3-Clause License

import typer
from rich.console import Console

import kr8s.asyncio
from kr8s.asyncio.objects import objects_from_files

console = Console()


# Missing Options
# TODO --allow-missing-template-keys='true'
# TODO --dry-run='none'
# TODO --edit='false'
# TODO --field-manager=''
# TODO -k, --kustomize=''
# TODO -o, --output=''
# TODO --raw='false'
# TODO -R, --recursive='false'
# TODO --save-config='false'
# TODO -l, --selector=''
# TODO --show-managed-fields='false'
# TODO --template=''
# TODO --validate='true'
# TODO --windows-line-endings='false'


async def create(
filename: str = typer.Option(
"",
"--filename",
"-f",
help="Filename, directory, or URL to files identifying the resources to create",
),
):
api = await kr8s.asyncio.api()
try:
objs = await objects_from_files(filename, api)
except Exception as e:
console.print(f"[red]Error loading objects from {filename}[/red]: {e}")
raise typer.Exit(1)
for obj in objs:
try:
await obj.create()
except Exception as e:
console.print(f"[red]Error creating {obj}[/red]: {e}")
raise typer.Exit(1)
console.print(f'[green]{obj.singular} "{obj}" created [/green]')
62 changes: 62 additions & 0 deletions examples/kubectl-ng/kubectl_ng/_delete.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# SPDX-FileCopyrightText: Copyright (c) 2023, Dask Developers, NVIDIA
# SPDX-License-Identifier: BSD 3-Clause License

import anyio
import typer
from rich.console import Console

import kr8s.asyncio
from kr8s.asyncio.objects import objects_from_files

console = Console()


# Missing Options
# TODO --all=false
# TODO -A, --all-namespaces=false
# TODO --cascade='background'
# TODO --dry-run='none'
# TODO --field-selector=''
# TODO --force=false
# TODO --grace-period=-1
# TODO --ignore-not-found=false
# TODO -k, --kustomize=''
# TODO --now='false'
# TODO -o, --output=''
# TODO --raw='false'
# TODO -R, --recursive='false'
# TODO -l, --selector=''
# TODO --timeout='0s'
# TODO --wait='true'


async def delete(
filename: str = typer.Option(
"",
"--filename",
"-f",
help="Filename, directory, or URL to files identifying the resources to delete",
),
wait: bool = typer.Option(
True,
"--wait",
help="If true, wait for resources to be gone before returning. This waits for finalizers.",
),
):
api = await kr8s.asyncio.api()
try:
objs = await objects_from_files(filename, api)
except Exception as e:
console.print(f"[red]Error loading objects from {filename}[/red]: {e}")
raise typer.Exit(1)
for obj in objs:
try:
await obj.delete()
except Exception as e:
console.print(f"[red]Error deleting {obj}[/red]: {e}")
raise typer.Exit(1)
console.print(f'[green]{obj.singular} "{obj}" deleted [/green]')
async with anyio.create_task_group() as tg:
for obj in objs:
if wait:
tg.start_soon(obj.wait, "delete")
4 changes: 4 additions & 0 deletions examples/kubectl-ng/kubectl_ng/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import typer

from ._api_resources import api_resources
from ._create import create
from ._delete import delete
from ._get import get
from ._version import version
from ._wait import wait
Expand All @@ -27,6 +29,8 @@ def register(app, func):

app = typer.Typer()
register(app, api_resources)
register(app, create)
register(app, delete)
register(app, get)
register(app, version)
register(app, wait)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app.kubernetes.io/name: proxy
spec:
containers:
- name: nginx
image: nginx:stable
ports:
- containerPort: 80
name: http-web-svc

---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app.kubernetes.io/name: proxy
ports:
- name: name-of-service-port
protocol: TCP
port: 80
targetPort: http-web-svc
34 changes: 34 additions & 0 deletions examples/kubectl-ng/kubectl_ng/tests/test_create_delete.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import pathlib

from kubectl_ng.cli import app
from typer.testing import CliRunner

from kr8s.objects import objects_from_files

runner = CliRunner()

HERE = pathlib.Path(__file__).parent.absolute()


def test_create_and_delete():
spec = str(HERE / "resources" / "simple" / "nginx_pod_service.yaml")

objs = objects_from_files(spec)
for obj in objs:
assert not obj.exists()

result = runner.invoke(app, ["create", "-f", spec])
assert result.exit_code == 0
for obj in objs:
assert obj.name in result.stdout

for obj in objs:
assert obj.exists()

result = runner.invoke(app, ["delete", "-f", spec])
assert result.exit_code == 0
for obj in objs:
assert obj.name in result.stdout

for obj in objs:
assert not obj.exists()
23 changes: 17 additions & 6 deletions kr8s/_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -1212,12 +1212,14 @@ def new_class(


def object_from_spec(
spec: dict, api: Api = None, allow_unknown_type: bool = False
spec: dict, api: Api = None, allow_unknown_type: bool = False, _asyncio: bool = True
) -> APIObject:
"""Create an APIObject from a Kubernetes resource spec.
Args:
spec: A Kubernetes resource spec.
allow_unknown_type: Whether to allow unknown resource types.
_asyncio: Whether to use asyncio or not.
Returns:
A corresponding APIObject subclass instance.
Expand All @@ -1226,7 +1228,7 @@ def object_from_spec(
ValueError: If the resource kind or API version is not supported.
"""
try:
cls = get_class(spec["kind"], spec["apiVersion"])
cls = get_class(spec["kind"], spec["apiVersion"], _asyncio=_asyncio)
except KeyError:
if allow_unknown_type:
cls = new_class(spec["kind"], spec["apiVersion"])
Expand All @@ -1236,12 +1238,15 @@ def object_from_spec(


async def object_from_name_type(
name: str, namespace: str = None, api: Api = None
name: str, namespace: str = None, api: Api = None, _asyncio: bool = True
) -> APIObject:
"""Create an APIObject from a Kubernetes resource name.
Args:
name: A Kubernetes resource name.
namespace: The namespace of the resource.
api: An optional API instance to use.
_asyncio: Whether to use asyncio or not.
Returns:
A corresponding APIObject subclass instance.
Expand All @@ -1258,19 +1263,23 @@ async def object_from_name_type(
else:
kind = resource_type
version = None
cls = get_class(kind, version)
cls = get_class(kind, version, _asyncio=_asyncio)
return await cls.get(name, namespace=namespace, api=api)


async def objects_from_files(
path: Union[str, pathlib.Path], api: Api = None, recursive: bool = False
path: Union[str, pathlib.Path],
api: Api = None,
recursive: bool = False,
_asyncio: bool = True,
) -> List[APIObject]:
"""Create APIObjects from Kubernetes resource files.
Args:
path: A path to a Kubernetes resource file or directory of resource files.
api: An optional API instance to use.
recursive: Whether to recursively search for resource files in subdirectories.
_asyncio: Whether to use asyncio or not.
Returns:
A list of APIObject subclass instances.
Expand All @@ -1290,6 +1299,8 @@ async def objects_from_files(
for doc in yaml.safe_load_all(f):
if doc is not None:
objects.append(
object_from_spec(doc, api=api, allow_unknown_type=True)
object_from_spec(
doc, api=api, allow_unknown_type=True, _asyncio=_asyncio
)
)
return objects
6 changes: 4 additions & 2 deletions kr8s/objects.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from functools import partial

from ._io import run_sync, sync
from ._objects import (
APIObject as _APIObject,
Expand Down Expand Up @@ -328,5 +330,5 @@ class Table(_Table):
_asyncio = False


object_from_name_type = run_sync(_object_from_name_type)
objects_from_files = run_sync(_objects_from_files)
object_from_name_type = run_sync(partial(_object_from_name_type, _asyncio=False))
objects_from_files = run_sync(partial(_objects_from_files, _asyncio=False))

0 comments on commit 842f760

Please sign in to comment.