Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support loading env vars from secret files #1994

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this

## [Unreleased]
### Added
* *Nothing*
* [#1868](https://github.com/shlinkio/shlink/issues/1868) Add support for [docker compose secrets](https://docs.docker.com/compose/use-secrets/) to the docker image.

### Changed
* [#1935](https://github.com/shlinkio/shlink/issues/1935) Replace dependency on abandoned `php-middleware/request-id` with userland simple middleware.
Expand Down
6 changes: 4 additions & 2 deletions bin/test/run-api-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ echo 'Starting server...'
[ "$TEST_RUNTIME" = 'openswoole' ] && vendor/bin/laminas mezzio:swoole:start -d
[ "$TEST_RUNTIME" = 'rr' ] && bin/rr serve -p -c=config/roadrunner/.rr.dev.yml \
-o=http.address=0.0.0.0:9999 \
-o=http.pool.debug=false \
-o=jobs.pool.debug=false \
-o=logs.encoding=json \
-o=logs.channels.http.encoding=json \
-o=logs.channels.server.encoding=json \
Expand All @@ -29,10 +31,10 @@ echo 'Starting server...'
sleep 2 # Let's give the server a couple of seconds to start

vendor/bin/phpunit --order-by=random -c phpunit-api.xml --testdox --colors=always --log-junit=build/coverage-api/junit.xml $*
testsExitCode=$?
TESTS_EXIT_CODE=$?

[ "$TEST_RUNTIME" = 'openswoole' ] && vendor/bin/laminas mezzio:swoole:stop
[ "$TEST_RUNTIME" = 'rr' ] && bin/rr stop -c config/roadrunner/.rr.dev.yml -o=http.address=0.0.0.0:9999

# Exit this script with the same code as the tests. If tests failed, this script has to fail
exit $testsExitCode
exit $TESTS_EXIT_CODE
23 changes: 22 additions & 1 deletion module/Core/src/Config/EnvVars.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@

namespace Shlinkio\Shlink\Core\Config;

use function file_get_contents;
use function is_file;
use function Shlinkio\Shlink\Config\env;
use function Shlinkio\Shlink\Config\parseEnvVar;
use function sprintf;

enum EnvVars: string
{
Expand Down Expand Up @@ -77,7 +81,24 @@ enum EnvVars: string

public function loadFromEnv(mixed $default = null): mixed
{
return env($this->value, $default);
return env($this->value) ?? $this->loadFromFileEnv() ?? $default;
}

/**
* Checks if an equivalent environment variable exists with the `_FILE` suffix. If so, it loads its value as a file,
* reads it, and returns its contents.
* This is useful when loading Shlink with docker compose and using secrets.
* See https://docs.docker.com/compose/use-secrets/
*/
private function loadFromFileEnv(): string|int|bool|null
{
$file = env(sprintf('%s_FILE', $this->value));
if ($file === null || ! is_file($file)) {
return null;
}

$content = file_get_contents($file);
return $content ? parseEnvVar($content) : null;
}

public function existsInEnv(): bool
Expand Down
10 changes: 10 additions & 0 deletions module/Core/test/Config/EnvVarsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@ protected function setUp(): void
{
putenv(EnvVars::BASE_PATH->value . '=the_base_path');
putenv(EnvVars::DB_NAME->value . '=shlink');

$envFilePath = __DIR__ . '/../DB_PASSWORD.env';
putenv(EnvVars::DB_PASSWORD->value . '_FILE=' . $envFilePath);
}

protected function tearDown(): void
{
putenv(EnvVars::BASE_PATH->value . '=');
putenv(EnvVars::DB_NAME->value . '=');
putenv(EnvVars::DB_PASSWORD->value . '_FILE=');
}

#[Test, DataProvider('provideExistingEnvVars')]
Expand Down Expand Up @@ -54,4 +58,10 @@ public static function provideEnvVarsValues(): iterable
yield 'DB_DRIVER without default' => [EnvVars::DB_DRIVER, null, null];
yield 'DB_DRIVER with default' => [EnvVars::DB_DRIVER, 'foobar', 'foobar'];
}

#[Test]
public function fallsBackToReadEnvVarsFromFile(): void
{
self::assertEquals('this_is_the_password', EnvVars::DB_PASSWORD->loadFromEnv());
}
}
1 change: 1 addition & 0 deletions module/Core/test/DB_PASSWORD.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
this_is_the_password
Loading