Umbrella Project, Phauxth |> project architecture |> non_PaaS_deployment

After finishing Elixir for Programmers (twice) by @pragdave, and studying the acme_bank repo, I want to start a toy umbrella project using phauxth and phoenix 1.3 and deploy it on a VPS (like DO), but do not know how to get started with an umbrella architecture with phauxth.

Looking into phauxth’s issue #22 (Phauxth and Umbrella Projects), installation isn’t as simple as mix phauxth.new my_app --umbrella

Questions:

  1. How should phauxth be installed in an --umbrella architecture?
    1.1 Should Authentication/Authorization concerns be in a separate app entirely with its own ecto/postgres dependency?
    1.2 Can/should websockets’ concerns be separated in its own app? See #3 below.
  2. Are there deployment concerns introduced by having an umbrella architecture, if deploying on a non-PaaS (i.e., VPS or dedicated)? I know this is a very broad question, but I’m asking after seeing the MasterProxy implementation for the heroku specific constraint.
  3. in @pragdave’s elixir course, there were 2 implementations of the UI, one as a server-side HTML app and the other utilizing websockets. But, I am wondering if it’s possible to have a websockets API server that strictly performs bi-direction communication without a UI? (e.g., communicate to the HTML (gallows) app as well as third-party clients (iOS/Android, IoT, etc.)?

Update for the curious:

The easiest way I found was to;

Setup umbrella app
Use Installer of phauxth in the apps dir
Modify mix and config files for umbrella structure

Now the only problem is ‘mix ecto.setup’ is not recognized at the top level. Any ideas? I have to go to individual app to run ecto.setup.

Or I have to manually ecto create and mix run priv seeds to get it working.

How can I run mix ecto.setup at the top level directory and have mix run it automatically?

Can you copy the ecto.setup alias into the root mix.exs file?

Thanks for the suggestion. However, it returned the same error.

** (Mix) The task "ecto.setup" could not be found. Did you mean "ecto.dump"?

I copied the following to the private aliases/0 function:

"ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"], "ecto.reset": ["ecto.drop", "ecto.setup"], "test": ["ecto.create --quiet", "ecto.migrate", "test"]

This got it working for me on a fresh umbrella project:

  def project do
    [
      apps_path: "apps",
      start_permanent: Mix.env() == :prod,
      deps: deps(),
      aliases: aliases() # <----- Add this line
    ]
  end

  defp deps do
    []
  end

  defp aliases do
    [
      "ecto.setup": ["ecto.create", "ecto.migrate", "run apps/foo/priv/repo/seeds.exs"] #<--- change path to seeds script
    ]
  end

Thanks. I think mix ecto.setup is now working, I just need to remove the duplicate aliases/0 entry in the app/foo’s mix.exs file.

The only problem now is that I can’t recreate a fresh installation of a working umbrella app with a phoenix app under it. Here’s what I did and the error I encountered:

mix new umbrella --umbrella
cd umbrella/apps
mix new baz
mix phx.new foo #fetch and install dependencies [n]
cd foo
mix phauxth.new --confirm --remember
cd ../../
mix deps.get
iex -S mix phx.server

I did not modify any mix.exs files or add aliases yet. I didn’t create the DB either, I just wanted to get the phoenix server up and running first from the root umbrella directory.

Here’s a snippet of the error during compilation.

Generated phoenix_ecto app
==> baz
Compiling 1 file (.ex)
Generated baz app
==> foo
Compiling 25 files (.ex)
Generated foo app
[error] GenServer Phoenix.CodeReloader.Server terminating
** (RuntimeError) trying to access Mix.Project.app_path for an umbrella project but umbrellas have no app
    (mix) lib/mix/project.ex:486: Mix.Project.app_path/1
    (phoenix) lib/phoenix/code_reloader/server.ex:28: Phoenix.CodeReloader.Server.handle_call/3
    (stdlib) gen_server.erl:636: :gen_server.try_handle_call/4
    (stdlib) gen_server.erl:665: :gen_server.handle_msg/6
    (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
Last message (from FooWeb.Endpoint): :check_symlinks
State: false
Client FooWeb.Endpoint is alive
    (stdlib) gen.erl:169: :gen.do_call/4
    (elixir) lib/gen_server.ex:831: GenServer.call/3
    (phoenix) lib/phoenix/endpoint/supervisor.ex:62: Phoenix.Endpoint.Supervisor.init/1
    (stdlib) supervisor.erl:294: :supervisor.init/1
    (stdlib) gen_server.erl:365: :gen_server.init_it/2
    (stdlib) gen_server.erl:333: :gen_server.init_it/6
    (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3

[info] Application foo exited: Foo.Application.start(:normal, []) returned an error: shutdown: failed to start child: FooWeb.Endpoint
    ** (EXIT) exited in: GenServer.call(Phoenix.CodeReloader.Server, :check_symlinks, :infinity)
        ** (EXIT) an exception was raised:
            ** (RuntimeError) trying to access Mix.Project.app_path for an umbrella project but umbrellas have no app
                (mix) lib/mix/project.ex:486: Mix.Project.app_path/1
                (phoenix) lib/phoenix/code_reloader/server.ex:28: Phoenix.CodeReloader.Server.handle_call/3
                (stdlib) gen_server.erl:636: :gen_server.try_handle_call/4
                (stdlib) gen_server.erl:665: :gen_server.handle_msg/6
                (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
** (Mix) Could not start application foo: Foo.Application.start(:normal, []) returned an error: shutdown: failed to start child: FooWeb.Endpoint
    ** (EXIT) exited in: GenServer.call(Phoenix.CodeReloader.Server, :check_symlinks, :infinity)
        ** (EXIT) an exception was raised:
            ** (RuntimeError) trying to access Mix.Project.app_path for an umbrella project but umbrellas have no app
                (mix) lib/mix/project.ex:486: Mix.Project.app_path/1
                (phoenix) lib/phoenix/code_reloader/server.ex:28: Phoenix.CodeReloader.Server.handle_call/3
                (stdlib) gen_server.erl:636: :gen_server.try_handle_call/4
                (stdlib) gen_server.erl:665: :gen_server.handle_msg/6
                (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3

folder tree

  • umbrella
    • apps
      • baz
      • foo/mix.exs
    • config/config.exs
  • mix.exs

mix.exs

defmodule Umbrella.MixProject do
  use Mix.Project

  def project do
    [
      apps_path: "apps",
      start_permanent: Mix.env() == :prod,
      deps: deps()
    ]
  end

 defp deps do
    []
  end
end

config/config.exs

use Mix.Config
import_config "../apps/*/config/config.exs"

apps/foo/mix.exs

defmodule Foo.Mixfile do
  use Mix.Project

  def project do
    [
      app: :foo,
      version: "0.0.1",
      build_path: "../../_build",
      config_path: "../../config/config.exs",
      deps_path: "../../deps",
      lockfile: "../../mix.lock",
      elixir: "~> 1.6",
      elixirc_paths: elixirc_paths(Mix.env),
      compilers: [:phoenix, :gettext] ++ Mix.compilers,
      start_permanent: Mix.env == :prod,
      aliases: aliases(),
      deps: deps()
    ]
  end

  def application do
    [
      mod: {Foo.Application, []},
      extra_applications: [:logger, :runtime_tools]
    ]
  end

  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_),     do: ["lib"]

  defp deps do
    [
      {:phoenix, "~> 1.3.1"},
      {:phoenix_pubsub, "~> 1.0"},
      {:phoenix_ecto, "~> 3.2"},
      {:postgrex, ">= 0.0.0"},
      {:phoenix_html, "~> 2.10"},
      {:phoenix_live_reload, "~> 1.0", only: :dev},
      {:gettext, "~> 0.11"},
      {:phauxth, "~> 1.2"},
      {:bcrypt_elixir, "~> 1.0"},
      {:bamboo, "~> 0.8"},
      {:cowboy, "~> 1.0"}
    ]
  end

  defp aliases do
    [
      "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
      "ecto.reset": ["ecto.drop", "ecto.setup"],
      "test": ["ecto.create --quiet", "ecto.migrate", "test"]
    ]
  end
end

Unfortunately v1.3.1 of Phoenix was recently released with an issue running phx.server from the root of an umbrella app. I’m using 1.3.0 for now.

This can be done by changing {:phoenix, "~> 1.3.0"} to {:phoenix, "1.3.0"} in the Phoenix app’s mix.exs file’s deps function until a new release is released.

3 Likes

thanks everyone! at least that resolved the issue for now while the fix is ported to the next release.