Skip to content

Commit 5e67774

Browse files
committed
chore(quickwit): add quickwit proxy
1 parent d387efb commit 5e67774

21 files changed

Lines changed: 194 additions & 143 deletions

File tree

charts/controller/templates/_helpers.tpl

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,11 @@ env:
5151
secretKeyRef:
5252
name: controller-creds
5353
key: django-secret-key
54-
- name: DRYCC_BUILDER_KEY
54+
- name: DRYCC_SERVICE_KEY
5555
valueFrom:
5656
secretKeyRef:
57-
name: builder-key-auth
58-
key: builder-key
57+
name: controller-creds
58+
key: service-key
5959
{{- if (.Values.valkeyUrl) }}
6060
- name: DRYCC_VALKEY_URL
6161
valueFrom:
@@ -119,18 +119,8 @@ env:
119119
name: controller-creds
120120
key: victoriametrics-url
121121
{{- else if .Values.victoriametrics.enabled }}
122-
- name: "DRYCC_VICTORIAMETRICS_USERNAME"
123-
valueFrom:
124-
secretKeyRef:
125-
name: victoriametrics-vmauth-creds
126-
key: username
127-
- name: "DRYCC_VICTORIAMETRICS_PASSWORD"
128-
valueFrom:
129-
secretKeyRef:
130-
name: victoriametrics-vmauth-creds
131-
key: password
132122
- name: "DRYCC_VICTORIAMETRICS_URL"
133-
value: "http://$(DRYCC_VICTORIAMETRICS_USERNAME):$(DRYCC_VICTORIAMETRICS_PASSWORD)@drycc-victoriametrics-vmauth.{{$.Release.Namespace}}.svc.{{$.Values.global.clusterDomain}}:8427"
123+
value: "http://drycc-victoriametrics-vmselect.{{$.Release.Namespace}}.svc.{{$.Values.global.clusterDomain}}:8481"
134124
{{- end }}
135125
{{- if .Values.passport.enabled }}
136126
- name: "DRYCC_PASSPORT_URL"
@@ -166,6 +156,10 @@ env:
166156
name: controller-creds
167157
key: passport-secret
168158
{{- end }}
159+
- name: QUICKWIT_SEARCHER_URL
160+
value: http://drycc-quickwit-searcher.{{ $.Release.Namespace }}.svc.{{ .Values.global.clusterDomain }}:7280
161+
- name: QUICKWIT_LOG_INDEX_PREFIX
162+
value: {{ .Values.quickwit.logIndexPrefix }}
169163
{{- range $key, $value := .Values.environment }}
170164
- name: {{ $key }}
171165
value: {{ $value | quote }}

charts/controller/templates/controller-secret-creds.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,6 @@ data:
3131
registry-username: {{ .Values.registryUsername | b64enc }}
3232
registry-password: {{ .Values.registryPassword | b64enc }}
3333
{{- end }}
34+
service-key: {{ (include "common.secrets.lookup" (dict "secret" "controller-creds" "key" "service-key" "defaultValue" (randAscii 64) "context" $)) }}
3435
django-secret-key: {{ (include "common.secrets.lookup" (dict "secret" "controller-creds" "key" "django-secret-key" "defaultValue" (randAscii 64) "context" $)) }}
3536
deploy-hook-secret-key: {{ (include "common.secrets.lookup" (dict "secret" "controller-creds" "key" "deploy-hook-secret-key" "defaultValue" (randAscii 64) "context" $)) }}

charts/controller/values.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,9 @@ valkey:
163163
database:
164164
enabled: true
165165

166+
quickwit:
167+
logIndexPrefix: logs-
168+
166169
registry:
167170
enabled: true
168171

rootfs/api/authentication.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ def authenticate(self, request):
2121
class DryccAuthentication(authentication.BaseAuthentication):
2222

2323
keywords = ('token', 'bearer')
24+
ignore_authentication_failed = False
2425

2526
def parse_header(self, request):
2627
try:
@@ -44,17 +45,19 @@ def authenticate(self, request):
4445
token_type, token = self.parse_header(request)
4546
if token_type is None or token is None:
4647
return None
47-
if token_type == 'bearer': # drycc oauth access token
48-
try:
48+
try:
49+
if token_type == 'bearer': # drycc oauth access token
4950
from api.backend import OauthCacheManager
5051
return OauthCacheManager().get_user(token), token
51-
except exceptions.AuthenticationFailed:
52-
return None
53-
# drycc token
54-
user = cache.get(token, None)
55-
if not user:
56-
return self.authenticate_credentials(token)
57-
return user, token
52+
# drycc token
53+
user = cache.get(token, None)
54+
if not user:
55+
return self.authenticate_credentials(token)
56+
return user, token
57+
except exceptions.AuthenticationFailed as e:
58+
if not self.ignore_authentication_failed:
59+
raise e
60+
return None
5861

5962
def authenticate_credentials(self, key):
6063
from api.models.base import Token

rootfs/api/consumers.py

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import time
33
import six
44
import ssl
5-
import aiohttp
65
import asyncio
76
from django.conf import settings
87
from django.core.cache import cache
@@ -66,47 +65,6 @@ def kubernetes(self):
6665
return core_v1_api.CoreV1Api()
6766

6867

69-
class AppLogsConsumer(BaseAppConsumer):
70-
71-
async def connect(self):
72-
await super().connect()
73-
self.session = None
74-
self.running = False
75-
self.conneted = True
76-
77-
async def task(self, **kwargs):
78-
lines = kwargs.get("lines", 100)
79-
follow = kwargs.get("follow", False)
80-
timeout = kwargs.get("timeout", 300)
81-
url = "http://{}:{}/logs/{}?log_lines={}&follow={}&timeout={}".format(
82-
settings.LOGGER_HOST, settings.LOGGER_PORT, self.id, lines, follow, timeout,
83-
)
84-
try:
85-
async with aiohttp.ClientSession() as session:
86-
self.session = session
87-
async with session.get(url) as response:
88-
async for data in response.content.iter_any():
89-
if not self.conneted:
90-
break
91-
await self.send(text_data=data)
92-
except asyncio.TimeoutError:
93-
pass
94-
finally:
95-
await self.close(code=1000)
96-
97-
async def receive(self, text_data=None, bytes_data=None):
98-
if self.running:
99-
return
100-
self.running = True
101-
kwargs = json.loads(text_data)
102-
asyncio.create_task(self.task(**kwargs))
103-
104-
async def disconnect(self, close_code):
105-
if self.session:
106-
await self.session.close()
107-
self.conneted = False
108-
109-
11068
class AppPodLogsConsumer(BaseK8sConsumer):
11169

11270
async def connect(self):

rootfs/api/models/app.py

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -250,8 +250,6 @@ def delete(self, *args, **kwargs):
250250
except KubeHTTPException:
251251
# it's fine if the namespace does not exist - delete app from the DB
252252
pass
253-
254-
self._clean_app_logs()
255253
return super(App, self).delete(*args, **kwargs)
256254

257255
def restart(self, **kwargs): # noqa
@@ -752,18 +750,6 @@ def __str__(self):
752750
def _get_deployment_name(self, ptype):
753751
return f"{self.id}-{ptype}"
754752

755-
def _clean_app_logs(self):
756-
"""Delete application logs stored by the logger component"""
757-
try:
758-
url = 'http://{}:{}/logs/{}'.format(settings.LOGGER_HOST,
759-
settings.LOGGER_PORT, self.id)
760-
requests.delete(url)
761-
except Exception as e:
762-
# Ignore errors deleting application logs. An error here should not interfere with
763-
# the overall success of deleting an application, but we should log it.
764-
err = 'Error deleting existing application logs: {}'.format(e)
765-
self.log(err, logging.WARNING)
766-
767753
def _mount(self, user, volume, app_settings, structure=None):
768754
volumes = Volume.objects.filter(app=self)
769755
tasks = []

rootfs/api/models/base.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import uuid
2+
import time
23
import string
34
import random
45
from datetime import timedelta
@@ -10,7 +11,7 @@
1011
from django.utils.translation import gettext_lazy as _
1112

1213

13-
from api.utils import validate_json, get_scheduler
14+
from api.utils import get_session, validate_json, get_scheduler
1415

1516
token_manager_oauth_schema = {
1617
"$schema": "http://json-schema.org/schema#",
@@ -47,6 +48,20 @@ class Meta:
4748
"""Mark :class:`AuditedModel` as abstract."""
4849
abstract = True
4950

51+
def app_log(self, app_id, msg):
52+
session = get_session()
53+
session.post(
54+
"",
55+
json={
56+
"timestamp": int(time.time()),
57+
"log": msg,
58+
"kubernetes": {
59+
"namespace": app_id,
60+
"container_name": "drycc-controller",
61+
}
62+
}
63+
)
64+
5065
@property
5166
def scheduler(self):
5267
annotations = {}

rootfs/api/permissions.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,20 +111,20 @@ def has_permission(self, request, view):
111111
return request.method in permissions.SAFE_METHODS or request.user.is_superuser
112112

113113

114-
class HasBuilderAuth(permissions.BasePermission):
114+
class IsServiceToken(permissions.BasePermission):
115115
"""
116-
View permission to allow builder to perform actions
117-
with a special HTTP header
116+
The service token is used for internal communication between Drycc components,
117+
such as the builder and Quickwit.
118118
"""
119119

120120
def has_permission(self, request, view):
121121
"""
122122
Return `True` if permission is granted, `False` otherwise.
123123
"""
124-
auth_header = request.META.get('HTTP_X_DRYCC_BUILDER_AUTH')
124+
auth_header = request.META.get('HTTP_X_DRYCC_SERVICE_KEY')
125125
if not auth_header:
126126
return False
127-
return auth_header == settings.BUILDER_KEY
127+
return auth_header == settings.SERVICE_KEY
128128

129129

130130
class IsWorkflowManager(permissions.BasePermission):

rootfs/api/routing.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@
44
from . import consumers
55

66
websocket_urlpatterns = [
7-
re_path(
8-
r'^v2/apps/(?P<id>([\w-]*))/logs/?$',
9-
consumers.AppLogsConsumer.as_asgi()),
107
re_path(
118
r'^v2/apps/(?P<id>([\w-]*))/pods/(?P<pod_id>([\w-]*))/logs/?$',
129
consumers.AppPodLogsConsumer.as_asgi()),

rootfs/api/settings/production.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,17 @@
44
import sys
55
import uuid
66
import json
7+
import random
8+
import string
79
import os.path
810
import tempfile
911
import dj_database_url
1012

13+
14+
def randstr(k):
15+
return ''.join(random.choices(string.ascii_lowercase + string.digits, k=k))
16+
17+
1118
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
1219
sys.path.insert(0, os.path.join(BASE_DIR, 'apps_extra'))
1320

@@ -312,10 +319,11 @@
312319
with open(DRYCC_VOLUME_CLAIM_TEMPLATE_PATH) as fd:
313320
DRYCC_VOLUME_CLAIM_TEMPLATE = json.load(fd)
314321

315-
# security keys and auth tokens
316-
random_secret = 'CHANGEME_sapm$s%upvsw5l_zuy_&29rkywd^78ff(qi*#@&*^'
317-
SECRET_KEY = os.environ.get('DRYCC_SECRET_KEY', random_secret)
318-
BUILDER_KEY = os.environ.get('DRYCC_BUILDER_KEY', random_secret)
322+
# Django secret key
323+
SECRET_KEY = os.environ.get('DRYCC_SECRET_KEY', randstr(64))
324+
325+
# Drycc service key
326+
SERVICE_KEY = os.environ.get('DRYCC_SERVICE_KEY', randstr(64))
319327

320328
# Drycc admission mutate key
321329
MUTATE_KEY_PATH = os.environ.get('DRYCC_MUTATE_KEY_PATH', '/etc/controller/mutate/cert/key')
@@ -387,10 +395,6 @@
387395
REGISTRY_LOCATION = os.environ.get('DRYCC_REGISTRY_LOCATION', 'on-cluster')
388396
REGISTRY_SECRET_PREFIX = os.environ.get('DRYCC_REGISTRY_SECRET_PREFIX', 'private-registry')
389397

390-
# logger settings
391-
LOGGER_HOST = os.environ.get('DRYCC_LOGGER_SERVICE_HOST', '127.0.0.1')
392-
LOGGER_PORT = os.environ.get('DRYCC_LOGGER_SERVICE_PORT_HTTP', 80)
393-
394398
DRYCC_DATABASE_URL = os.environ.get('DRYCC_DATABASE_URL', 'postgres://postgres:@:5432/drycc')
395399
DATABASES = {
396400
'default': dj_database_url.config(default=DRYCC_DATABASE_URL)
@@ -479,6 +483,10 @@
479483
}
480484
}
481485

486+
# Quickwit Configuration
487+
QUICKWIT_SEARCHER_URL = os.environ.get('QUICKWIT_SEARCHER_URL', None)
488+
QUICKWIT_LOG_INDEX_PREFIX = os.environ.get('QUICKWIT_LOG_INDEX_PREFIX', None)
489+
482490
# Workflow-manager Configuration Options
483491
WORKFLOW_MANAGER_URL = os.environ.get('WORKFLOW_MANAGER_URL', None)
484492
WORKFLOW_MANAGER_ACCESS_KEY = os.environ.get('WORKFLOW_MANAGER_ACCESS_KEY', None)

0 commit comments

Comments
 (0)