From b8a566f4a0343ac5daee881323acc2998e6f8c7e Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Wed, 21 Apr 2021 11:34:48 +0200 Subject: [PATCH] outposts: move local connection check to task, run every 60 minutes Signed-off-by: Jens Langhammer --- authentik/outposts/apps.py | 56 ++------------------ authentik/outposts/settings.py | 5 ++ authentik/outposts/tasks.py | 48 +++++++++++++++++ tests/e2e/test_provider_proxy.py | 4 +- tests/integration/test_outpost_docker.py | 4 +- tests/integration/test_outpost_kubernetes.py | 4 +- tests/integration/test_proxy_docker.py | 4 +- tests/integration/test_proxy_kubernetes.py | 4 +- 8 files changed, 66 insertions(+), 63 deletions(-) diff --git a/authentik/outposts/apps.py b/authentik/outposts/apps.py index 02cce0e16..ac93c2ab6 100644 --- a/authentik/outposts/apps.py +++ b/authentik/outposts/apps.py @@ -1,17 +1,8 @@ """authentik outposts app config""" from importlib import import_module -from os import R_OK, access -from os.path import expanduser -from pathlib import Path -from socket import gethostname -from urllib.parse import urlparse -import yaml from django.apps import AppConfig from django.db import ProgrammingError -from docker.constants import DEFAULT_UNIX_SOCKET -from kubernetes.config.incluster_config import SERVICE_TOKEN_FILENAME -from kubernetes.config.kube_config import KUBE_CONFIG_DEFAULT_LOCATION from structlog.stdlib import get_logger LOGGER = get_logger() @@ -27,49 +18,8 @@ class AuthentikOutpostConfig(AppConfig): def ready(self): import_module("authentik.outposts.signals") try: - AuthentikOutpostConfig.init_local_connection() + from authentik.outposts.tasks import outpost_local_connection + + outpost_local_connection.delay() except ProgrammingError: pass - - @staticmethod - def init_local_connection(): - """Check if local kubernetes or docker connections should be created""" - from authentik.outposts.models import ( - DockerServiceConnection, - KubernetesServiceConnection, - ) - - # Explicitly check against token filename, as thats - # only present when the integration is enabled - if Path(SERVICE_TOKEN_FILENAME).exists(): - LOGGER.debug("Detected in-cluster Kubernetes Config") - if not KubernetesServiceConnection.objects.filter(local=True).exists(): - LOGGER.debug("Created Service Connection for in-cluster") - KubernetesServiceConnection.objects.create( - name="Local Kubernetes Cluster", local=True, kubeconfig={} - ) - # For development, check for the existence of a kubeconfig file - kubeconfig_path = expanduser(KUBE_CONFIG_DEFAULT_LOCATION) - if Path(kubeconfig_path).exists(): - LOGGER.debug("Detected kubeconfig") - kubeconfig_local_name = f"k8s-{gethostname()}" - if not KubernetesServiceConnection.objects.filter( - name=kubeconfig_local_name - ).exists(): - LOGGER.debug("Creating kubeconfig Service Connection") - with open(kubeconfig_path, "r") as _kubeconfig: - KubernetesServiceConnection.objects.create( - name=kubeconfig_local_name, - kubeconfig=yaml.safe_load(_kubeconfig), - ) - unix_socket_path = urlparse(DEFAULT_UNIX_SOCKET).path - socket = Path(unix_socket_path) - if socket.exists() and access(socket, R_OK): - LOGGER.debug("Detected local docker socket") - if len(DockerServiceConnection.objects.filter(local=True)) == 0: - LOGGER.debug("Created Service Connection for docker") - DockerServiceConnection.objects.create( - name="Local Docker connection", - local=True, - url=unix_socket_path, - ) diff --git a/authentik/outposts/settings.py b/authentik/outposts/settings.py index d7e0fc6e8..617274295 100644 --- a/authentik/outposts/settings.py +++ b/authentik/outposts/settings.py @@ -17,4 +17,9 @@ CELERY_BEAT_SCHEDULE = { "schedule": crontab(minute="*/5"), "options": {"queue": "authentik_scheduled"}, }, + "outpost_local_connection": { + "task": "authentik.outposts.tasks.outpost_local_connection", + "schedule": crontab(minute="*/60"), + "options": {"queue": "authentik_scheduled"}, + }, } diff --git a/authentik/outposts/tasks.py b/authentik/outposts/tasks.py index 1c5426eb9..ece820add 100644 --- a/authentik/outposts/tasks.py +++ b/authentik/outposts/tasks.py @@ -1,11 +1,20 @@ """outpost tasks""" +from os import R_OK, access +from os.path import expanduser +from pathlib import Path +from socket import gethostname from typing import Any +from urllib.parse import urlparse +import yaml from asgiref.sync import async_to_sync from channels.layers import get_channel_layer from django.core.cache import cache from django.db.models.base import Model from django.utils.text import slugify +from docker.constants import DEFAULT_UNIX_SOCKET +from kubernetes.config.incluster_config import SERVICE_TOKEN_FILENAME +from kubernetes.config.kube_config import KUBE_CONFIG_DEFAULT_LOCATION from structlog.stdlib import get_logger from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus @@ -185,3 +194,42 @@ def _outpost_single_update(outpost: Outpost, layer=None): for state in OutpostState.for_outpost(outpost): LOGGER.debug("sending update", channel=state.uid, outpost=outpost) async_to_sync(layer.send)(state.uid, {"type": "event.update"}) + + +@CELERY_APP.task() +def outpost_local_connection(): + """Checks the local environment and create Service connections.""" + # Explicitly check against token filename, as thats + # only present when the integration is enabled + if Path(SERVICE_TOKEN_FILENAME).exists(): + LOGGER.debug("Detected in-cluster Kubernetes Config") + if not KubernetesServiceConnection.objects.filter(local=True).exists(): + LOGGER.debug("Created Service Connection for in-cluster") + KubernetesServiceConnection.objects.create( + name="Local Kubernetes Cluster", local=True, kubeconfig={} + ) + # For development, check for the existence of a kubeconfig file + kubeconfig_path = expanduser(KUBE_CONFIG_DEFAULT_LOCATION) + if Path(kubeconfig_path).exists(): + LOGGER.debug("Detected kubeconfig") + kubeconfig_local_name = f"k8s-{gethostname()}" + if not KubernetesServiceConnection.objects.filter( + name=kubeconfig_local_name + ).exists(): + LOGGER.debug("Creating kubeconfig Service Connection") + with open(kubeconfig_path, "r") as _kubeconfig: + KubernetesServiceConnection.objects.create( + name=kubeconfig_local_name, + kubeconfig=yaml.safe_load(_kubeconfig), + ) + unix_socket_path = urlparse(DEFAULT_UNIX_SOCKET).path + socket = Path(unix_socket_path) + if socket.exists() and access(socket, R_OK): + LOGGER.debug("Detected local docker socket") + if len(DockerServiceConnection.objects.filter(local=True)) == 0: + LOGGER.debug("Created Service Connection for docker") + DockerServiceConnection.objects.create( + name="Local Docker connection", + local=True, + url=unix_socket_path, + ) diff --git a/tests/e2e/test_provider_proxy.py b/tests/e2e/test_provider_proxy.py index 1e5554eaf..cbced9b70 100644 --- a/tests/e2e/test_provider_proxy.py +++ b/tests/e2e/test_provider_proxy.py @@ -13,13 +13,13 @@ from selenium.webdriver.common.by import By from authentik import __version__ from authentik.core.models import Application from authentik.flows.models import Flow -from authentik.outposts.apps import AuthentikOutpostConfig from authentik.outposts.models import ( DockerServiceConnection, Outpost, OutpostConfig, OutpostType, ) +from authentik.outposts.tasks import outpost_local_connection from authentik.providers.proxy.models import ProxyProvider from tests.e2e.utils import SeleniumTestCase, apply_migration, object_manager, retry @@ -117,7 +117,7 @@ class TestProviderProxyConnect(ChannelsLiveServerTestCase): @object_manager def test_proxy_connectivity(self): """Test proxy connectivity over websocket""" - AuthentikOutpostConfig.init_local_connection() + outpost_local_connection() proxy: ProxyProvider = ProxyProvider.objects.create( name="proxy_provider", authorization_flow=Flow.objects.get( diff --git a/tests/integration/test_outpost_docker.py b/tests/integration/test_outpost_docker.py index 8a1c4dc69..82cdfe84c 100644 --- a/tests/integration/test_outpost_docker.py +++ b/tests/integration/test_outpost_docker.py @@ -12,9 +12,9 @@ from docker.types.healthcheck import Healthcheck from authentik import __version__ from authentik.crypto.models import CertificateKeyPair from authentik.flows.models import Flow -from authentik.outposts.apps import AuthentikOutpostConfig from authentik.outposts.controllers.docker import DockerController from authentik.outposts.models import DockerServiceConnection, Outpost, OutpostType +from authentik.outposts.tasks import outpost_local_connection from authentik.providers.proxy.models import ProxyProvider @@ -53,7 +53,7 @@ class OutpostDockerTests(TestCase): self.ssl_folder = mkdtemp() self.container = self._start_container(self.ssl_folder) # Ensure that local connection have been created - AuthentikOutpostConfig.init_local_connection() + outpost_local_connection() self.provider: ProxyProvider = ProxyProvider.objects.create( name="test", internal_host="http://localhost", diff --git a/tests/integration/test_outpost_kubernetes.py b/tests/integration/test_outpost_kubernetes.py index 99345849c..a82261854 100644 --- a/tests/integration/test_outpost_kubernetes.py +++ b/tests/integration/test_outpost_kubernetes.py @@ -3,11 +3,11 @@ from django.test import TestCase from authentik.flows.models import Flow from authentik.lib.config import CONFIG -from authentik.outposts.apps import AuthentikOutpostConfig from authentik.outposts.controllers.k8s.base import NeedsUpdate from authentik.outposts.controllers.k8s.deployment import DeploymentReconciler from authentik.outposts.controllers.kubernetes import KubernetesController from authentik.outposts.models import KubernetesServiceConnection, Outpost, OutpostType +from authentik.outposts.tasks import outpost_local_connection from authentik.providers.proxy.models import ProxyProvider @@ -17,7 +17,7 @@ class OutpostKubernetesTests(TestCase): def setUp(self): super().setUp() # Ensure that local connection have been created - AuthentikOutpostConfig.init_local_connection() + outpost_local_connection() self.provider: ProxyProvider = ProxyProvider.objects.create( name="test", internal_host="http://localhost", diff --git a/tests/integration/test_proxy_docker.py b/tests/integration/test_proxy_docker.py index 532810230..b52bb8c73 100644 --- a/tests/integration/test_proxy_docker.py +++ b/tests/integration/test_proxy_docker.py @@ -12,8 +12,8 @@ from docker.types.healthcheck import Healthcheck from authentik import __version__ from authentik.crypto.models import CertificateKeyPair from authentik.flows.models import Flow -from authentik.outposts.apps import AuthentikOutpostConfig from authentik.outposts.models import DockerServiceConnection, Outpost, OutpostType +from authentik.outposts.tasks import outpost_local_connection from authentik.providers.proxy.controllers.docker import DockerController from authentik.providers.proxy.models import ProxyProvider @@ -53,7 +53,7 @@ class TestProxyDocker(TestCase): self.ssl_folder = mkdtemp() self.container = self._start_container(self.ssl_folder) # Ensure that local connection have been created - AuthentikOutpostConfig.init_local_connection() + outpost_local_connection() self.provider: ProxyProvider = ProxyProvider.objects.create( name="test", internal_host="http://localhost", diff --git a/tests/integration/test_proxy_kubernetes.py b/tests/integration/test_proxy_kubernetes.py index 61b9a92aa..11b319696 100644 --- a/tests/integration/test_proxy_kubernetes.py +++ b/tests/integration/test_proxy_kubernetes.py @@ -3,8 +3,8 @@ import yaml from django.test import TestCase from authentik.flows.models import Flow -from authentik.outposts.apps import AuthentikOutpostConfig from authentik.outposts.models import KubernetesServiceConnection, Outpost, OutpostType +from authentik.outposts.tasks import outpost_local_connection from authentik.providers.proxy.controllers.kubernetes import ProxyKubernetesController from authentik.providers.proxy.models import ProxyProvider @@ -14,7 +14,7 @@ class TestProxyKubernetes(TestCase): def setUp(self): # Ensure that local connection have been created - AuthentikOutpostConfig.init_local_connection() + outpost_local_connection() def test_kubernetes_controller_static(self): """Test Kubernetes Controller"""