Improve preflight file permissions check (#114)
* Added startup permissions check with more helpful message * Updated README * Fixes faulty test
This commit is contained in:
@@ -14,7 +14,6 @@ defmodule Pinchflat.Boot.PreJobStartupTasks do
|
||||
|
||||
alias Pinchflat.Repo
|
||||
alias Pinchflat.Settings
|
||||
alias Pinchflat.Filesystem.FilesystemHelpers
|
||||
|
||||
def start_link(opts \\ []) do
|
||||
GenServer.start_link(__MODULE__, %{}, opts)
|
||||
@@ -33,7 +32,6 @@ defmodule Pinchflat.Boot.PreJobStartupTasks do
|
||||
def init(state) do
|
||||
reset_executing_jobs()
|
||||
apply_default_settings()
|
||||
ensure_directories_are_writeable()
|
||||
rename_old_job_workers()
|
||||
|
||||
{:ok, state}
|
||||
@@ -56,21 +54,6 @@ defmodule Pinchflat.Boot.PreJobStartupTasks do
|
||||
Settings.fetch!(:pro_enabled, false)
|
||||
end
|
||||
|
||||
defp ensure_directories_are_writeable do
|
||||
directories = [
|
||||
Application.get_env(:pinchflat, :media_directory),
|
||||
Application.get_env(:pinchflat, :tmpfile_directory),
|
||||
Application.get_env(:pinchflat, :metadata_directory)
|
||||
]
|
||||
|
||||
Enum.each(directories, fn dir ->
|
||||
file = Path.join([dir, ".keep"])
|
||||
|
||||
# This will fail if the directory is not writeable, stopping boot
|
||||
FilesystemHelpers.write_p!(file, "")
|
||||
end)
|
||||
end
|
||||
|
||||
# As part of a large refactor, I ended up moving a bunch of workers around. This
|
||||
# is a problem because the workers are stored in the database and the runner
|
||||
# will try to run the OLD jobs. This is also why these tasks run before the job
|
||||
|
||||
@@ -20,6 +20,21 @@ defmodule Pinchflat.Filesystem.FilesystemHelpers do
|
||||
filepath
|
||||
end
|
||||
|
||||
@doc """
|
||||
Writes content to a file, creating directories as needed.
|
||||
Takes the same args as File.write/3.
|
||||
|
||||
Returns :ok | {:error, any()}
|
||||
"""
|
||||
def write_p(file, content, modes \\ []) do
|
||||
dirname = Path.dirname(file)
|
||||
|
||||
case File.mkdir_p(dirname) do
|
||||
:ok -> File.write(file, content, modes)
|
||||
err -> err
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Writes content to a file, creating directories as needed.
|
||||
Takes the same args as File.write!/3.
|
||||
@@ -27,11 +42,7 @@ defmodule Pinchflat.Filesystem.FilesystemHelpers do
|
||||
Returns :ok | raises on error
|
||||
"""
|
||||
def write_p!(filepath, content, modes \\ []) do
|
||||
filepath
|
||||
|> Path.dirname()
|
||||
|> File.mkdir_p!()
|
||||
|
||||
File.write!(filepath, content, modes)
|
||||
:ok = write_p(filepath, content, modes)
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
||||
@@ -5,6 +5,10 @@ defmodule Pinchflat.Release do
|
||||
"""
|
||||
@app :pinchflat
|
||||
|
||||
require Logger
|
||||
|
||||
alias Pinchflat.Filesystem.FilesystemHelpers
|
||||
|
||||
def migrate do
|
||||
load_app()
|
||||
|
||||
@@ -18,6 +22,36 @@ defmodule Pinchflat.Release do
|
||||
{:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :down, to: version))
|
||||
end
|
||||
|
||||
def check_file_permissions do
|
||||
load_app()
|
||||
|
||||
directories = [
|
||||
"/config",
|
||||
"/downloads",
|
||||
Application.get_env(:pinchflat, :media_directory),
|
||||
Application.get_env(:pinchflat, :tmpfile_directory),
|
||||
Application.get_env(:pinchflat, :metadata_directory)
|
||||
]
|
||||
|
||||
Enum.each(directories, fn dir ->
|
||||
Logger.info("Checking permissions for #{dir}")
|
||||
filepath = Path.join([dir, ".keep"])
|
||||
|
||||
case FilesystemHelpers.write_p(filepath, "") do
|
||||
:ok ->
|
||||
Logger.info("Permissions OK")
|
||||
|
||||
{:error, :eacces} ->
|
||||
Logger.error(permission_denied_screed(dir))
|
||||
raise "Permission denied"
|
||||
|
||||
err ->
|
||||
Logger.error("Permissions check failed: #{inspect(err)}")
|
||||
raise "Unknown error"
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
defp repos do
|
||||
Application.fetch_env!(@app, :ecto_repos)
|
||||
end
|
||||
@@ -25,4 +59,31 @@ defmodule Pinchflat.Release do
|
||||
defp load_app do
|
||||
Application.load(@app)
|
||||
end
|
||||
|
||||
defp permission_denied_screed(dir) do
|
||||
"""
|
||||
The directory "#{dir}" is not writeable by the Docker container.
|
||||
|
||||
Please ensure that the directory exists and is writeable by the Docker
|
||||
container. All setups are different, but you may be able to run something
|
||||
like this on the *host*:
|
||||
|
||||
chown nobody -R <host path that maps to #{dir}>
|
||||
chmod 755 -R <host path that maps to #{dir}>
|
||||
|
||||
Swapping in your real host path. Then, you should set the user running
|
||||
this container by editing your `docker run` command like so:
|
||||
|
||||
docker run --user 99:100 <rest of the command>
|
||||
|
||||
Or adding `user: '99:100'` to the Pinchflat service of your Docker Compose
|
||||
file. Again, there are many ways to do this depending on your setup and
|
||||
this is just one example. See issue #106 in the Pinchflat Github for more.
|
||||
|
||||
No matter the case, this _is_ a permissions error and allowing the container
|
||||
to write to the directory is the only way to fix it. It is not recommended
|
||||
to run the container as `root` because files created by Pinchflat may not
|
||||
be accessible to other apps that want to modify them.
|
||||
"""
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user