outposts: fix defaults and tests for outposts
This commit is contained in:
parent
706448dc14
commit
3b76af4eaa
|
@ -16,9 +16,9 @@ from passbook import __version__
|
|||
from passbook.core.models import Application
|
||||
from passbook.flows.models import Flow
|
||||
from passbook.outposts.models import (
|
||||
DockerServiceConnection,
|
||||
Outpost,
|
||||
OutpostConfig,
|
||||
OutpostDeploymentType,
|
||||
OutpostType,
|
||||
)
|
||||
from passbook.providers.proxy.models import ProxyProvider
|
||||
|
@ -76,7 +76,6 @@ class TestProviderProxy(SeleniumTestCase):
|
|||
outpost: Outpost = Outpost.objects.create(
|
||||
name="proxy_outpost",
|
||||
type=OutpostType.PROXY,
|
||||
deployment_type=OutpostDeploymentType.CUSTOM,
|
||||
)
|
||||
outpost.providers.add(proxy)
|
||||
outpost.save()
|
||||
|
@ -128,10 +127,11 @@ class TestProviderProxyConnect(ChannelsLiveServerTestCase):
|
|||
proxy.save()
|
||||
# we need to create an application to actually access the proxy
|
||||
Application.objects.create(name="proxy", slug="proxy", provider=proxy)
|
||||
service_connection = DockerServiceConnection.objects.get(local=True)
|
||||
outpost: Outpost = Outpost.objects.create(
|
||||
name="proxy_outpost",
|
||||
type=OutpostType.PROXY,
|
||||
deployment_type=OutpostDeploymentType.DOCKER,
|
||||
service_connection=service_connection,
|
||||
_config=asdict(
|
||||
OutpostConfig(passbook_host=self.live_server_url, log_level="debug")
|
||||
),
|
||||
|
|
|
@ -28,11 +28,11 @@ class PassbookOutpostConfig(AppConfig):
|
|||
import_module("passbook.outposts.signals")
|
||||
try:
|
||||
self.init_local_connection()
|
||||
except (ProgrammingError):
|
||||
except ProgrammingError:
|
||||
pass
|
||||
|
||||
def init_local_connection(self):
|
||||
# Check if local kubernetes or docker connections should be created
|
||||
"""Check if local kubernetes or docker connections should be created"""
|
||||
from passbook.outposts.models import (
|
||||
KubernetesServiceConnection,
|
||||
DockerServiceConnection,
|
||||
|
|
|
@ -20,8 +20,9 @@ class BaseController:
|
|||
outpost: Outpost
|
||||
connection: OutpostServiceConnection
|
||||
|
||||
def __init__(self, outpost: Outpost):
|
||||
def __init__(self, outpost: Outpost, connection: OutpostServiceConnection):
|
||||
self.outpost = outpost
|
||||
self.connection = connection
|
||||
self.logger = get_logger()
|
||||
self.deployment_ports = {}
|
||||
|
||||
|
|
|
@ -23,8 +23,8 @@ class DockerController(BaseController):
|
|||
|
||||
image_base = "beryju/passbook"
|
||||
|
||||
def __init__(self, outpost: Outpost) -> None:
|
||||
super().__init__(outpost)
|
||||
def __init__(self, outpost: Outpost, connection: DockerServiceConnection) -> None:
|
||||
super().__init__(outpost, connection)
|
||||
try:
|
||||
if self.connection.local:
|
||||
self.client = DockerClient.from_env()
|
||||
|
|
|
@ -25,8 +25,10 @@ class KubernetesController(BaseController):
|
|||
|
||||
connection: KubernetesServiceConnection
|
||||
|
||||
def __init__(self, outpost: Outpost) -> None:
|
||||
super().__init__(outpost)
|
||||
def __init__(
|
||||
self, outpost: Outpost, connection: KubernetesServiceConnection
|
||||
) -> None:
|
||||
super().__init__(outpost, connection)
|
||||
try:
|
||||
if self.connection.local:
|
||||
load_incluster_config()
|
||||
|
|
|
@ -10,7 +10,9 @@ def fix_missing_token_identifier(apps: Apps, schema_editor: BaseDatabaseSchemaEd
|
|||
Token = apps.get_model("passbook_core", "Token")
|
||||
from passbook.outposts.models import Outpost
|
||||
|
||||
for outpost in Outpost.objects.using(schema_editor.connection.alias).all().only('pk'):
|
||||
for outpost in (
|
||||
Outpost.objects.using(schema_editor.connection.alias).all().only("pk")
|
||||
):
|
||||
user_identifier = outpost.user_identifier
|
||||
user = User.objects.get(username=user_identifier)
|
||||
tokens = Token.objects.filter(user=user)
|
||||
|
|
|
@ -11,16 +11,23 @@ from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
|||
def migrate_to_service_connection(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
|
||||
db_alias = schema_editor.connection.alias
|
||||
Outpost = apps.get_model("passbook_outposts", "Outpost")
|
||||
DockerServiceConnection = apps.get_model("passbook_outposts", "DockerServiceConnection")
|
||||
KubernetesServiceConnection = apps.get_model("passbook_outposts", "KubernetesServiceConnection")
|
||||
DockerServiceConnection = apps.get_model(
|
||||
"passbook_outposts", "DockerServiceConnection"
|
||||
)
|
||||
KubernetesServiceConnection = apps.get_model(
|
||||
"passbook_outposts", "KubernetesServiceConnection"
|
||||
)
|
||||
from passbook.outposts.apps import PassbookOutpostConfig
|
||||
|
||||
# Ensure that local connection have been created
|
||||
PassbookOutpostConfig.init_local_connection(None)
|
||||
|
||||
docker = DockerServiceConnection.objects.filter(local=True)
|
||||
k8s = KubernetesServiceConnection.objects.filter(local=True)
|
||||
|
||||
for outpost in Outpost.objects.using(db_alias).all().exclude(deployment_type="custom"):
|
||||
for outpost in (
|
||||
Outpost.objects.using(db_alias).all().exclude(deployment_type="custom")
|
||||
):
|
||||
if outpost.deployment_type == "kubernetes":
|
||||
outpost.service_connection = k8s
|
||||
elif outpost.deployment_type == "docker":
|
||||
|
@ -31,43 +38,85 @@ def migrate_to_service_connection(apps: Apps, schema_editor: BaseDatabaseSchemaE
|
|||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('passbook_outposts', '0009_fix_missing_token_identifier'),
|
||||
("passbook_outposts", "0009_fix_missing_token_identifier"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='OutpostServiceConnection',
|
||||
name="OutpostServiceConnection",
|
||||
fields=[
|
||||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('name', models.TextField()),
|
||||
('local', models.BooleanField(default=False, help_text='If enabled, use the local connection. Required Docker socket/Kubernetes Integration', unique=True)),
|
||||
(
|
||||
"uuid",
|
||||
models.UUIDField(
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
),
|
||||
),
|
||||
("name", models.TextField()),
|
||||
(
|
||||
"local",
|
||||
models.BooleanField(
|
||||
default=False,
|
||||
help_text="If enabled, use the local connection. Required Docker socket/Kubernetes Integration",
|
||||
unique=True,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='DockerServiceConnection',
|
||||
name="DockerServiceConnection",
|
||||
fields=[
|
||||
('outpostserviceconnection_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='passbook_outposts.outpostserviceconnection')),
|
||||
('url', models.TextField()),
|
||||
('tls', models.BooleanField()),
|
||||
(
|
||||
"outpostserviceconnection_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="passbook_outposts.outpostserviceconnection",
|
||||
),
|
||||
),
|
||||
("url", models.TextField()),
|
||||
("tls", models.BooleanField()),
|
||||
],
|
||||
bases=('passbook_outposts.outpostserviceconnection',),
|
||||
bases=("passbook_outposts.outpostserviceconnection",),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='KubernetesServiceConnection',
|
||||
name="KubernetesServiceConnection",
|
||||
fields=[
|
||||
('outpostserviceconnection_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='passbook_outposts.outpostserviceconnection')),
|
||||
('config', models.JSONField()),
|
||||
(
|
||||
"outpostserviceconnection_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="passbook_outposts.outpostserviceconnection",
|
||||
),
|
||||
),
|
||||
("config", models.JSONField()),
|
||||
],
|
||||
bases=('passbook_outposts.outpostserviceconnection',),
|
||||
bases=("passbook_outposts.outpostserviceconnection",),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='outpost',
|
||||
name='service_connection',
|
||||
field=models.ForeignKey(blank=True, default=None, help_text='Select Service-Connection passbook should use to manage this outpost. Leave empty if passbook should not handle the deployment.', null=True, on_delete=django.db.models.deletion.SET_DEFAULT, to='passbook_outposts.outpostserviceconnection'),
|
||||
model_name="outpost",
|
||||
name="service_connection",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
default=None,
|
||||
help_text="Select Service-Connection passbook should use to manage this outpost. Leave empty if passbook should not handle the deployment.",
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_DEFAULT,
|
||||
to="passbook_outposts.outpostserviceconnection",
|
||||
),
|
||||
),
|
||||
migrations.RunPython(migrate_to_service_connection),
|
||||
migrations.RemoveField(
|
||||
model_name='outpost',
|
||||
name='deployment_type',
|
||||
model_name="outpost",
|
||||
name="deployment_type",
|
||||
),
|
||||
]
|
||||
|
|
|
@ -18,6 +18,7 @@ from packaging.version import LegacyVersion, Version, parse
|
|||
from passbook import __version__
|
||||
from passbook.core.models import Provider, Token, TokenIntents, User
|
||||
from passbook.lib.config import CONFIG
|
||||
from passbook.lib.models import InheritanceForeignKey
|
||||
from passbook.lib.utils.template import render_to_string
|
||||
|
||||
OUR_VERSION = parse(__version__)
|
||||
|
@ -106,7 +107,7 @@ class Outpost(models.Model):
|
|||
name = models.TextField()
|
||||
|
||||
type = models.TextField(choices=OutpostType.choices, default=OutpostType.PROXY)
|
||||
service_connection = models.ForeignKey(
|
||||
service_connection = InheritanceForeignKey(
|
||||
OutpostServiceConnection,
|
||||
default=None,
|
||||
null=True,
|
||||
|
|
|
@ -10,7 +10,14 @@ from structlog import get_logger
|
|||
from passbook.lib.tasks import MonitoredTask, TaskResult, TaskResultStatus
|
||||
from passbook.lib.utils.reflection import path_to_class
|
||||
from passbook.outposts.controllers.base import ControllerException
|
||||
from passbook.outposts.models import Outpost, OutpostModel, OutpostState, OutpostType
|
||||
from passbook.outposts.models import (
|
||||
DockerServiceConnection,
|
||||
KubernetesServiceConnection,
|
||||
Outpost,
|
||||
OutpostModel,
|
||||
OutpostState,
|
||||
OutpostType,
|
||||
)
|
||||
from passbook.providers.proxy.controllers.docker import ProxyDockerController
|
||||
from passbook.providers.proxy.controllers.kubernetes import ProxyKubernetesController
|
||||
from passbook.root.celery import CELERY_APP
|
||||
|
@ -33,10 +40,13 @@ def outpost_controller(self: MonitoredTask, outpost_pk: str):
|
|||
self.set_uid(slugify(outpost.name))
|
||||
try:
|
||||
if outpost.type == OutpostType.PROXY:
|
||||
if outpost.deployment_type == OutpostDeploymentType.KUBERNETES:
|
||||
logs = ProxyKubernetesController(outpost).up_with_logs()
|
||||
if outpost.deployment_type == OutpostDeploymentType.DOCKER:
|
||||
logs = ProxyDockerController(outpost).up_with_logs()
|
||||
service_connection = outpost.service_connection
|
||||
if isinstance(service_connection, DockerServiceConnection):
|
||||
logs = ProxyDockerController(outpost, service_connection).up_with_logs()
|
||||
if isinstance(service_connection, KubernetesServiceConnection):
|
||||
logs = ProxyKubernetesController(
|
||||
outpost, service_connection
|
||||
).up_with_logs()
|
||||
except ControllerException as exc:
|
||||
self.set_status(TaskResult(TaskResultStatus.ERROR).with_error(exc))
|
||||
else:
|
||||
|
@ -48,10 +58,11 @@ def outpost_pre_delete(outpost_pk: str):
|
|||
"""Delete outpost objects before deleting the DB Object"""
|
||||
outpost = Outpost.objects.get(pk=outpost_pk)
|
||||
if outpost.type == OutpostType.PROXY:
|
||||
if outpost.deployment_type == OutpostDeploymentType.KUBERNETES:
|
||||
ProxyKubernetesController(outpost).down()
|
||||
if outpost.deployment_type == OutpostDeploymentType.DOCKER:
|
||||
ProxyDockerController(outpost).down()
|
||||
service_connection = outpost.service_connection
|
||||
if isinstance(service_connection, DockerServiceConnection):
|
||||
ProxyDockerController(outpost, service_connection).down()
|
||||
if isinstance(service_connection, KubernetesServiceConnection):
|
||||
ProxyKubernetesController(outpost, service_connection).down()
|
||||
|
||||
|
||||
@CELERY_APP.task()
|
||||
|
|
|
@ -11,7 +11,7 @@ from passbook.flows.models import Flow
|
|||
from passbook.outposts.controllers.k8s.base import NeedsUpdate
|
||||
from passbook.outposts.controllers.k8s.deployment import DeploymentReconciler
|
||||
from passbook.outposts.controllers.kubernetes import KubernetesController
|
||||
from passbook.outposts.models import Outpost, OutpostDeploymentType, OutpostType
|
||||
from passbook.outposts.models import KubernetesServiceConnection, Outpost, OutpostType
|
||||
from passbook.providers.proxy.models import ProxyProvider
|
||||
|
||||
|
||||
|
@ -29,7 +29,6 @@ class OutpostTests(TestCase):
|
|||
outpost: Outpost = Outpost.objects.create(
|
||||
name="test",
|
||||
type=OutpostType.PROXY,
|
||||
deployment_type=OutpostDeploymentType.CUSTOM,
|
||||
)
|
||||
|
||||
# Before we add a provider, the user should only have access to the outpost
|
||||
|
@ -79,17 +78,18 @@ class OutpostKubernetesTests(TestCase):
|
|||
external_host="http://localhost",
|
||||
authorization_flow=Flow.objects.first(),
|
||||
)
|
||||
self.service_connection = KubernetesServiceConnection.objects.get(local=True)
|
||||
self.outpost: Outpost = Outpost.objects.create(
|
||||
name="test",
|
||||
type=OutpostType.PROXY,
|
||||
deployment_type=OutpostDeploymentType.KUBERNETES,
|
||||
service_connection=self.service_connection,
|
||||
)
|
||||
self.outpost.providers.add(self.provider)
|
||||
self.outpost.save()
|
||||
|
||||
def test_deployment_reconciler(self):
|
||||
"""test that deployment requires update"""
|
||||
controller = KubernetesController(self.outpost)
|
||||
controller = KubernetesController(self.outpost, self.service_connection)
|
||||
deployment_reconciler = DeploymentReconciler(controller)
|
||||
|
||||
self.assertIsNotNone(deployment_reconciler.retrieve())
|
||||
|
|
|
@ -12,7 +12,12 @@ from structlog import get_logger
|
|||
|
||||
from passbook.core.models import User
|
||||
from passbook.outposts.controllers.docker import DockerController
|
||||
from passbook.outposts.models import Outpost, OutpostType
|
||||
from passbook.outposts.models import (
|
||||
DockerServiceConnection,
|
||||
KubernetesServiceConnection,
|
||||
Outpost,
|
||||
OutpostType,
|
||||
)
|
||||
from passbook.providers.proxy.controllers.kubernetes import ProxyKubernetesController
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
@ -35,7 +40,7 @@ class DockerComposeView(LoginRequiredMixin, View):
|
|||
)
|
||||
manifest = ""
|
||||
if outpost.type == OutpostType.PROXY:
|
||||
controller = DockerController(outpost)
|
||||
controller = DockerController(outpost, DockerServiceConnection())
|
||||
manifest = controller.get_static_deployment()
|
||||
|
||||
return HttpResponse(manifest, content_type="text/vnd.yaml")
|
||||
|
@ -53,7 +58,9 @@ class KubernetesManifestView(LoginRequiredMixin, View):
|
|||
)
|
||||
manifest = ""
|
||||
if outpost.type == OutpostType.PROXY:
|
||||
controller = ProxyKubernetesController(outpost)
|
||||
controller = ProxyKubernetesController(
|
||||
outpost, KubernetesServiceConnection()
|
||||
)
|
||||
manifest = controller.get_static_deployment()
|
||||
|
||||
return HttpResponse(manifest, content_type="text/vnd.yaml")
|
||||
|
|
|
@ -3,15 +3,15 @@ from typing import Dict
|
|||
from urllib.parse import urlparse
|
||||
|
||||
from passbook.outposts.controllers.docker import DockerController
|
||||
from passbook.outposts.models import Outpost
|
||||
from passbook.outposts.models import DockerServiceConnection, Outpost
|
||||
from passbook.providers.proxy.models import ProxyProvider
|
||||
|
||||
|
||||
class ProxyDockerController(DockerController):
|
||||
"""Proxy Provider Docker Contoller"""
|
||||
|
||||
def __init__(self, outpost: Outpost):
|
||||
super().__init__(outpost)
|
||||
def __init__(self, outpost: Outpost, connection: DockerServiceConnection):
|
||||
super().__init__(outpost, connection)
|
||||
self.deployment_ports = {
|
||||
"http": 4180,
|
||||
"https": 4443,
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
"""Proxy Provider Kubernetes Contoller"""
|
||||
from passbook.outposts.controllers.kubernetes import KubernetesController
|
||||
from passbook.outposts.models import Outpost
|
||||
from passbook.outposts.models import KubernetesServiceConnection, Outpost
|
||||
from passbook.providers.proxy.controllers.k8s.ingress import IngressReconciler
|
||||
|
||||
|
||||
class ProxyKubernetesController(KubernetesController):
|
||||
"""Proxy Provider Kubernetes Contoller"""
|
||||
|
||||
def __init__(self, outpost: Outpost):
|
||||
super().__init__(outpost)
|
||||
def __init__(self, outpost: Outpost, connection: KubernetesServiceConnection):
|
||||
super().__init__(outpost, connection)
|
||||
self.deployment_ports = {
|
||||
"http": 4180,
|
||||
"https": 4443,
|
||||
|
|
|
@ -6,7 +6,7 @@ import yaml
|
|||
from django.test import TestCase
|
||||
|
||||
from passbook.flows.models import Flow
|
||||
from passbook.outposts.models import Outpost, OutpostDeploymentType, OutpostType
|
||||
from passbook.outposts.models import KubernetesServiceConnection, Outpost, OutpostType
|
||||
from passbook.providers.proxy.controllers.kubernetes import ProxyKubernetesController
|
||||
from passbook.providers.proxy.models import ProxyProvider
|
||||
|
||||
|
@ -23,15 +23,16 @@ class TestControllers(TestCase):
|
|||
external_host="http://localhost",
|
||||
authorization_flow=Flow.objects.first(),
|
||||
)
|
||||
service_connection = KubernetesServiceConnection.objects.get(local=True)
|
||||
outpost: Outpost = Outpost.objects.create(
|
||||
name="test",
|
||||
type=OutpostType.PROXY,
|
||||
deployment_type=OutpostDeploymentType.KUBERNETES,
|
||||
service_connection=service_connection,
|
||||
)
|
||||
outpost.providers.add(provider)
|
||||
outpost.save()
|
||||
|
||||
controller = ProxyKubernetesController(outpost)
|
||||
controller = ProxyKubernetesController(outpost, service_connection)
|
||||
manifest = controller.get_static_deployment()
|
||||
self.assertEqual(len(list(yaml.load_all(manifest, Loader=yaml.SafeLoader))), 4)
|
||||
|
||||
|
@ -43,14 +44,15 @@ class TestControllers(TestCase):
|
|||
external_host="http://localhost",
|
||||
authorization_flow=Flow.objects.first(),
|
||||
)
|
||||
service_connection = KubernetesServiceConnection.objects.get(local=True)
|
||||
outpost: Outpost = Outpost.objects.create(
|
||||
name="test",
|
||||
type=OutpostType.PROXY,
|
||||
deployment_type=OutpostDeploymentType.KUBERNETES,
|
||||
service_connection=service_connection,
|
||||
)
|
||||
outpost.providers.add(provider)
|
||||
outpost.save()
|
||||
|
||||
controller = ProxyKubernetesController(outpost)
|
||||
controller = ProxyKubernetesController(outpost, service_connection)
|
||||
controller.up()
|
||||
controller.down()
|
||||
|
|
Reference in a new issue