fix: improve creation of containers

This commit is contained in:
Michel Roux 2024-11-04 11:04:25 +01:00
parent 83fbc483f0
commit f6ba1ac476

View File

@ -52,6 +52,7 @@ class SerializedContainer(BaseModel):
labels: dict[str, str] labels: dict[str, str]
status: str status: str
health: str health: str
engine: str | None
owner: str | None owner: str | None
environment: list[str] environment: list[str]
@ -64,24 +65,46 @@ def serialize_container(container: Container) -> SerializedContainer:
labels=container.labels, labels=container.labels,
status=container.status, status=container.status,
health=container.health, health=container.health,
engine=container.labels.get("engine"),
owner=container.labels.get("owner"), owner=container.labels.get("owner"),
environment=container.attrs["Config"]["Env"], environment=container.attrs["Config"]["Env"],
) )
def select_container(
container_name: str, credentials: Annotated[HTTPBasicCredentials, Depends(security)]
) -> Container:
try:
container = client.containers.get(container_name)
except errors.APIError:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
if (
credentials.username != "admin"
and container.labels.get("engine") != "pilotwings"
and container.labels.get("owner") != credentials.username
):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
return container
@app.get("/api/containers") @app.get("/api/containers")
def get_containers( def get_containers(
credentials: Annotated[HTTPBasicCredentials, Depends(security)], credentials: Annotated[HTTPBasicCredentials, Depends(security)],
) -> list[SerializedContainer]: ) -> list[SerializedContainer]:
if credentials.username == "admin": if credentials.username == "admin":
return [ return [
serialize_container(container) for container in client.containers.list() serialize_container(container)
for container in client.containers.list(
filters={"label": ["engine=pilotwings"]}
)
] ]
return [ return [
serialize_container(container) serialize_container(container)
for container in client.containers.list( for container in client.containers.list(
filters={"label": f"owner={credentials.username}"} filters={"label": ["engine=pilotwings", f"owner={credentials.username}"]},
) )
] ]
@ -91,76 +114,40 @@ def get_container(
container_name: str, container_name: str,
credentials: Annotated[HTTPBasicCredentials, Depends(security)], credentials: Annotated[HTTPBasicCredentials, Depends(security)],
) -> SerializedContainer: ) -> SerializedContainer:
try: return serialize_container(select_container(container_name, credentials))
container = client.containers.get(container_name)
except errors.APIError:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
if (
credentials.username != "admin"
and f"owner={credentials.username}" not in container.labels
):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
return serialize_container(container)
class UpdateContainerRequest(BaseModel): class ContainerRequest(BaseModel):
image: str
environment: dict[str, str] environment: dict[str, str]
class CreateContainerRequest(UpdateContainerRequest):
image: str
@app.post("/api/container/{container_name}") @app.post("/api/container/{container_name}")
def create_container( def create_or_update_container(
container_name: str, container_name: str,
request_body: CreateContainerRequest, request_body: ContainerRequest,
credentials: Annotated[HTTPBasicCredentials, Depends(security)], credentials: Annotated[HTTPBasicCredentials, Depends(security)],
) -> SerializedContainer: ) -> SerializedContainer:
networks = client.networks.list(names=["pilotwings"])
if not networks:
client.networks.create("pilotwings")
try:
container = select_container(container_name, credentials)
container.stop()
container.remove(v=True, force=True)
except errors.APIError:
pass
return serialize_container( return serialize_container(
client.containers.run( client.containers.run(
request_body.image, request_body.image,
detach=True, detach=True,
environment=request_body.environment, environment=request_body.environment,
labels={"owner": credentials.username}, labels={"engine": "pilotwings", "owner": credentials.username},
name=container_name,
restart_policy={"Name": "always"},
)
)
@app.put("/api/container/{container_name}")
def update_container(
container_name: str,
request_body: UpdateContainerRequest,
credentials: Annotated[HTTPBasicCredentials, Depends(security)],
) -> SerializedContainer:
try:
container = client.containers.get(container_name)
except errors.APIError:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
if (
credentials.username != "admin"
and f"owner={credentials.username}" not in container.labels
):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
if not container.image:
raise HTTPException(status_code=status.HTTP_410_GONE)
container.stop()
container.remove(v=True, force=True)
return serialize_container(
client.containers.run(
container.image.tags[0],
detach=True,
environment=request_body.environment,
labels={"owner": credentials.username},
name=container_name, name=container_name,
network="pilotwings",
restart_policy={"Name": "always"}, restart_policy={"Name": "always"},
) )
) )