Skip to content

Commit

Permalink
Enable getting Stomp authentication credentials from the environment (#…
Browse files Browse the repository at this point in the history
…374)

- Removes the mounting of a manually formatted secret containing
the credentials
  • Loading branch information
DiamondJoseph authored Feb 27, 2024
1 parent 598d789 commit edb8ac5
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 27 deletions.
13 changes: 3 additions & 10 deletions helm/blueapi/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,6 @@ spec:
sources:
- configMap:
name: {{ include "blueapi.fullname" . }}-config
{{- with .Values.existingSecret }}
- secret:
name: {{ . }}
{{- end }}
{{- if .Values.scratch.hostPath }}
- name: scratch-host
hostPath:
Expand All @@ -62,14 +58,11 @@ spec:
args:
- "-c"
- "/config/config.yaml"
{{- with .Values.existingSecret }}
- "-c"
- "/config/secret.yaml"
{{- end }}
- "serve"
env:
- name: SCRATCH_AREA
value: {{ .Values.scratch.containerPath }}
{{- if .Values.extraEnvVars }}
{{- tpl .Values.extraEnvVars . | nindent 10 }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
Expand Down
12 changes: 0 additions & 12 deletions helm/blueapi/templates/secret.yaml

This file was deleted.

12 changes: 10 additions & 2 deletions helm/blueapi/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,6 @@ tolerations: []

affinity: {}

#existingSecret: see templates/secret.yaml

hostNetwork: false # May be needed for talking to arcane protocols such as EPICS

listener:
Expand All @@ -78,6 +76,16 @@ scratch:
hostPath: "" # example: /usr/local/blueapi-software-scratch
containerPath: /blueapi-plugins/scratch

# Additional envVars to mount to the pod as a String
extraEnvVars: |
- name: SCRATCH_AREA
value: {{ .Values.scratch.containerPath }}
# - name: RABBITMQ_PASSWORD
# valueFrom:
# secretKeyRef:
# name: rabbitmq-password
# key: rabbitmq-password
worker:
api:
host: 0.0.0.0 # Allow non-loopback traffic
Expand Down
14 changes: 12 additions & 2 deletions src/blueapi/config.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import os
from enum import Enum
from pathlib import Path
from typing import Any, Dict, Generic, Literal, Mapping, Optional, Type, TypeVar, Union

import yaml
from pydantic import BaseModel, Field, ValidationError, parse_obj_as
from pydantic import BaseModel, Field, ValidationError, parse_obj_as, validator

from blueapi.utils import BlueapiBaseModel, InvalidConfigError

Expand All @@ -23,12 +24,21 @@ class Source(BaseModel):

class BasicAuthentication(BaseModel):
"""
Log in details for when a server uses authentication
Log in details for when a server uses authentication.
If username or passcode match exactly the regex ^\\${(.*)}$
they attempt to replace with an environment variable of the same.
i.e. ${foo} or ${FOO} are replaced with the value of FOO
"""

username: str = "guest"
passcode: str = "guest"

@validator("username", "passcode")
def get_from_env(cls, v: str):
if v.startswith("${") and v.endswith("}"):
return os.environ[v.removeprefix("${").removesuffix("}").upper()]
return v


class StompConfig(BaseModel):
"""
Expand Down
32 changes: 31 additions & 1 deletion tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
from pathlib import Path
from typing import Any, Type

import mock
import pytest
from pydantic import BaseModel, Field

from blueapi.config import ApplicationConfig, ConfigLoader
from blueapi.config import ApplicationConfig, BasicAuthentication, ConfigLoader
from blueapi.utils import InvalidConfigError


Expand Down Expand Up @@ -126,3 +127,32 @@ def test_example_config_yaml_gives_same_config_as_model(default_yaml: Path):
yaml_config = loader.load()

assert default_config == yaml_config


@mock.patch.dict(os.environ, {"FOO": "bar"}, clear=True)
def test_auth_from_env():
auth = BasicAuthentication(username="${FOO}", passcode="baz")
assert auth.username == "bar"


@mock.patch.dict(os.environ, {"FOO": "bar", "BAZ": "qux"}, clear=True)
def test_auth_from_env_repeated_key():
auth = BasicAuthentication(username="${FOO}", passcode="${FOO}")
assert auth.username == "bar"
assert auth.passcode == "bar"


@mock.patch.dict(os.environ, {"FOO": "bar"}, clear=True)
def test_auth_from_env_ignore_case():
auth = BasicAuthentication(username="${FOO}", passcode="${foo}")
assert auth.username == "bar"
assert auth.passcode == "bar"


@mock.patch.dict(os.environ, {"FOO": "bar"}, clear=True)
def test_auth_from_env_throws_when_not_available():
# Eagerly throws an exception, will fail during initial loading
with pytest.raises(KeyError):
BasicAuthentication(username="${BAZ}", passcode="baz")
with pytest.raises(KeyError):
BasicAuthentication(username="${baz}", passcode="baz")

0 comments on commit edb8ac5

Please sign in to comment.