outposts: implement docker controller

This commit is contained in:
Jens Langhammer 2020-10-04 00:36:12 +02:00
parent d3a96ac7aa
commit d506e8f1a3
7 changed files with 135 additions and 47 deletions

View file

@ -1,36 +0,0 @@
"""Docker Compose controller"""
from yaml import safe_dump
from passbook import __version__
from passbook.outposts.controllers.base import BaseController
class DockerComposeController(BaseController):
"""Docker Compose controller"""
image_base = "beryju/passbook"
def run(self):
self.logger.warning("DockerComposeController does not implement run")
raise NotImplementedError
def get_static_deployment(self) -> str:
"""Generate docker-compose yaml for proxy, version 3.5"""
ports = [f"{x}:{x}" for _, x in self.deployment_ports.items()]
compose = {
"version": "3.5",
"services": {
f"passbook_{self.outpost.type}": {
"image": f"{self.image_base}-{self.outpost.type}:{__version__}",
"ports": ports,
"environment": {
"PASSBOOK_HOST": self.outpost.config.passbook_host,
"PASSBOOK_INSECURE": str(
self.outpost.config.passbook_host_insecure
),
"PASSBOOK_TOKEN": self.outpost.token.token_uuid.hex,
},
}
},
}
return safe_dump(compose, default_flow_style=False)

View file

@ -0,0 +1,76 @@
"""Docker controller"""
from docker import DockerClient, from_env
from docker.errors import NotFound
from docker.models.containers import Container
from yaml import safe_dump
from passbook import __version__
from passbook.outposts.controllers.base import BaseController
class DockerController(BaseController):
"""Docker controller"""
client: DockerClient
container: Container
image_base = "beryju/passbook"
def __init__(self, outpost_pk: str) -> None:
super().__init__(outpost_pk)
self.client = from_env()
def _get_container(self) -> Container:
container_name = f"passbook-proxy-{self.outpost.uuid.hex}"
try:
return self.client.containers.get(container_name)
except NotFound:
return self.client.containers.create(
image=f"{self.image_base}-{self.outpost.type}:{__version__}",
name=f"passbook-proxy-{self.outpost.uuid.hex}",
detach=True,
ports={x: x for _, x in self.deployment_ports.items()},
environment={
"PASSBOOK_HOST": self.outpost.config.passbook_host,
"PASSBOOK_INSECURE": str(
self.outpost.config.passbook_host_insecure
),
"PASSBOOK_TOKEN": self.outpost.token.token_uuid.hex,
},
)
def run(self):
container = self._get_container()
# Check if the container is out of date, delete it and retry
if len(container.image.tags) > 0:
tag: str = container.iamge.tags[0]
_, _, version = tag.partition(":")
if version != __version__:
container.kill()
container.remove(force=True)
return self.run()
if container.status != "running":
container.start()
return None
def get_static_deployment(self) -> str:
"""Generate docker-compose yaml for proxy, version 3.5"""
ports = [f"{x}:{x}" for _, x in self.deployment_ports.items()]
compose = {
"version": "3.5",
"services": {
f"passbook_{self.outpost.type}": {
"image": f"{self.image_base}-{self.outpost.type}:{__version__}",
"ports": ports,
"environment": {
"PASSBOOK_HOST": self.outpost.config.passbook_host,
"PASSBOOK_INSECURE": str(
self.outpost.config.passbook_host_insecure
),
"PASSBOOK_TOKEN": self.outpost.token.token_uuid.hex,
},
}
},
}
return safe_dump(compose, default_flow_style=False)

View file

@ -0,0 +1,26 @@
# Generated by Django 3.1.2 on 2020-10-03 22:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("passbook_outposts", "0005_auto_20200909_1733"),
]
operations = [
migrations.AlterField(
model_name="outpost",
name="deployment_type",
field=models.TextField(
choices=[
("kubernetes", "Kubernetes"),
("docker", "Docker"),
("custom", "Custom"),
],
default="custom",
help_text="Select between passbook-managed deployment types or a custom deployment.",
),
),
]

View file

@ -59,7 +59,8 @@ class OutpostType(models.TextChoices):
class OutpostDeploymentType(models.TextChoices):
"""Deployment types that are managed through passbook"""
# KUBERNETES = "kubernetes"
KUBERNETES = "kubernetes"
DOCKER = "docker"
CUSTOM = "custom"

View file

@ -12,6 +12,7 @@ from passbook.outposts.models import (
OutpostModel,
OutpostType,
)
from passbook.providers.proxy.controllers.docker import ProxyDockerController
from passbook.providers.proxy.controllers.kubernetes import ProxyKubernetesController
from passbook.root.celery import CELERY_APP
@ -20,20 +21,27 @@ LOGGER = get_logger()
@CELERY_APP.task(bind=True)
# pylint: disable=unused-argument
def outpost_k8s_controller(self):
"""Launch Kubernetes Controller for all Outposts which are deployed in kubernetes"""
for outpost in Outpost.objects.filter(
deployment_type=OutpostDeploymentType.KUBERNETES
def outpost_controller(self):
"""Launch Controller for all Outposts which support it"""
for outpost in Outpost.objects.exclude(
deployment_type=OutpostDeploymentType.CUSTOM
):
outpost_k8s_controller_single.delay(outpost.pk.hex, outpost.type)
outpost_controller_single.delay(
outpost.pk.hex, outpost.deployment_type, outpost.type
)
@CELERY_APP.task(bind=True)
# pylint: disable=unused-argument
def outpost_k8s_controller_single(self, outpost: str, outpost_type: str):
"""Launch Kubernetes manager and reconcile deployment/service/etc"""
def outpost_controller_single(
self, outpost: str, deployment_type: str, outpost_type: str
):
"""Launch controller and reconcile deployment/service/etc"""
if outpost_type == OutpostType.PROXY:
ProxyKubernetesController(outpost).run()
if deployment_type == OutpostDeploymentType.KUBERNETES:
ProxyKubernetesController(outpost).run()
if deployment_type == OutpostDeploymentType.DOCKER:
ProxyDockerController(outpost).run()
@CELERY_APP.task()

View file

@ -11,7 +11,7 @@ from guardian.shortcuts import get_objects_for_user
from structlog import get_logger
from passbook.core.models import User
from passbook.outposts.controllers.compose import DockerComposeController
from passbook.outposts.controllers.docker import DockerController
from passbook.outposts.models import Outpost, OutpostType
from passbook.providers.proxy.controllers.kubernetes import ProxyKubernetesController
@ -35,7 +35,7 @@ class DockerComposeView(LoginRequiredMixin, View):
)
manifest = ""
if outpost.type == OutpostType.PROXY:
controller = DockerComposeController(outpost_pk)
controller = DockerController(outpost_pk)
manifest = controller.get_static_deployment()
return HttpResponse(manifest, content_type="text/vnd.yaml")

View file

@ -0,0 +1,13 @@
"""Proxy Provider Docker Contoller"""
from passbook.outposts.controllers.docker import DockerController
class ProxyDockerController(DockerController):
"""Proxy Provider Docker Contoller"""
def __init__(self, outpost_pk: str):
super().__init__(outpost_pk)
self.deployment_ports = {
"http": 4180,
"https": 4443,
}