71 lines
3.3 KiB
Python
71 lines
3.3 KiB
Python
"""Reject unsafe release coordinates and keep secrets out of public release files."""
|
|
|
|
from dataclasses import replace
|
|
from pathlib import Path
|
|
import tempfile
|
|
import unittest
|
|
|
|
from dotenv import dotenv_values
|
|
|
|
from deploy.gray_config import GrayConfig, write_release
|
|
|
|
|
|
class GrayConfigTests(unittest.TestCase):
|
|
def settings(self):
|
|
return {
|
|
'PUBLIC_ORIGIN': 'https://chat.example.com', 'HOST_PORT': '29013',
|
|
'GRAY_DEPLOY_DIR': '/data/services/gray-logistics-chat',
|
|
'GRAY_PROXY_CONF_DIR': '/etc/nginx/conf.d',
|
|
'GRAY_TLS_CERT_FILE': '/etc/ssl/example/fullchain.pem',
|
|
'GRAY_TLS_KEY_FILE': '/etc/ssl/example/privkey.pem',
|
|
'SOURCE_COMMIT': 'a' * 40, 'BUILD_NUMBER': '7',
|
|
'CHAT_IMAGE': 'baiyue-logistics-chat-gray:' + 'a' * 12 + '-b7',
|
|
'CHAT_IMAGE_ID': 'sha256:' + 'b' * 64,
|
|
'CERTBOT_IMAGE_ID': 'sha256:' + 'c' * 64,
|
|
}
|
|
|
|
def test_unsafe_origins_paths_and_mismatched_images_are_rejected(self):
|
|
cases = (
|
|
('PUBLIC_ORIGIN', 'http://chat.example.com'),
|
|
('PUBLIC_ORIGIN', 'https://user:password@chat.example.com'),
|
|
('PUBLIC_ORIGIN', 'https://chat.example.com/admin'),
|
|
('GRAY_DEPLOY_DIR', '/data/services/../gray-logistics-chat'),
|
|
('GRAY_DEPLOY_DIR', '/data/shared-services'),
|
|
('GRAY_PROXY_CONF_DIR', '/etc/nginx; echo unsafe'),
|
|
('HOST_PORT', '443'), ('CHAT_IMAGE', 'another-service:latest'),
|
|
('SOURCE_COMMIT', 'main'), ('CHAT_IMAGE_ID', 'unverified-image'),
|
|
)
|
|
for key, value in cases:
|
|
with self.subTest(setting=key, value=value):
|
|
settings = self.settings()
|
|
settings[key] = value
|
|
with self.assertRaises(ValueError):
|
|
GrayConfig.from_environment(settings)
|
|
|
|
def test_data_paths_remain_stable_across_releases_and_credentials_stay_separate(self):
|
|
config = GrayConfig.from_environment(self.settings())
|
|
updated = replace(config, release=config.root + '/releases/next-build')
|
|
for release in (config, updated):
|
|
self.assertIn('CHAT_DATA_DIR=' + config.root + '/data\n', release.release_environment())
|
|
self.assertIn('CHAT_LOG_DIR=' + config.root + '/logs\n', release.release_environment())
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
path = Path(directory) / 'release'
|
|
key = "fixture-${literal}-don't-expand"
|
|
write_release(path, config, key)
|
|
self.assertEqual(dotenv_values(path / 'runtime.env', interpolate=False)['DEEPSEEK_API_KEY'], key)
|
|
for name in ('release.env', 'site.conf', 'compose.yml', 'activate.sh'):
|
|
self.assertNotIn(key, (path / name).read_text(encoding='utf-8'))
|
|
self.assertIn('auth_basic_user_file', (path / 'site.conf').read_text(encoding='utf-8'))
|
|
|
|
def test_invalid_model_credential_does_not_create_a_release_directory(self):
|
|
config = GrayConfig.from_environment(self.settings())
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
path = Path(directory) / 'release'
|
|
with self.assertRaises(ValueError):
|
|
write_release(path, config, 'fixture-key\nANOTHER_SETTING=bad')
|
|
self.assertFalse(path.exists())
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|