Modifying relup with with edeliver and distillery

Hello,

The goal is to increase deployment process understanding and to run migrations prior to hot upgrade in Phoenix umbrella app deployed with distillery and edeliver.

After reading https://github.com/edeliver/edeliver/issues/102 and looking into the code my understanding is that there should be 2 modules to make it happen:

  1. overridden MigrationModification that will add second module to the relup file thus making “system” (edeliver?) to execute it during specific point at the upgrade process
  2. module implementing RunnableInstruction behavior that will actually run the migrations

I assume that first module is needed during release assembly process and second should be compiled in the applications and accessible at runtime.

So I put them into apps/xxx_web/lib/relup as upgrade_modification.ex and run_migrations.ex with exactly the sane content that bharendt provided in the comment https://github.com/edeliver/edeliver/issues/102#issuecomment-226312574 (it makes sense!) but nothing happend.
I also tried to add def usable?(_config), do: true but that didn’t help as well.

Any suggestions?

UPDATE: I added a plugin AppName.Relup.UpgradeModification directive to the distillery config as it pointed out in https://github.com/edeliver/edeliver/pull/248/files and now can see

==> Modifying relup file...

==> Using AppName.Relup.UpgradeModification module for relup modification.

during deployment but migrations are still pending.

Seems that modify_relup is not called, since I can’t see neither info message nor a file being created

defmodule AppName.Relup.UpgradeModification do
  use Edeliver.Relup.Modification
  require Logger

  def modify_relup(instructions, config) do
     Logger.info "Running custom release modifications..."
    File.touch("/tmp/upgrade-modifications")
    instructions |> Edeliver.Relup.PhoenixModification.modify_relup(config)
                 |> AppName.Relup.Instructions.RunMigrations.modify_relup(config)
  end

  def priority, do: priority_user()
  def usable?(_config), do: true
end

Also, module responsible for running migrations AppName.Relup.Instructions.RunMigrations doesn’t show up in relup

I’m the one that submitted that PR you referenced. I actually had to do 3 things to get edeliver to function properly and run my migrations:

First, add plugin Releases.Plugin.ModifyRelup to distillery’s config. It looks like you’re directly referencing your custom upgrade modification instead of Releases.Plugin.ModifyRelup which may be your problem.

Next, create a module in your project with the modification instructions (I don’t think the name of it matters, as long as the module has use Edeliver.Relup.Modification so edeliver knows to use it during deployment). Here is my module:

defmodule MyApp.Deploy.Edeliver.Modification do
  use Edeliver.Relup.Modification

  def modify_relup(instructions = %Edeliver.Relup.Instructions{}, config = %{}) do
    instructions
    |> MyApp.Deploy.Edeliver.MigrationInstruction.modify_relup(config)
    # I actually was never able to make Edeliver.Relup.PhoenixModification run properly without a bunch
    # of errors relating to CheckRanchAcceptors, but everything seems to work fine with hot upgrades
  end

  def priority, do: priority_user()
end

Your modification looks similar. It references AppName.Relup.Instructions.RunMigrations.modify_relup — did you actually make that module/function in your app? This was the third step for me, adding this module:

defmodule MyApp.Deploy.Edeliver.MigrationInstruction do
  use Edeliver.Relup.RunnableInstruction

  def insert_where, do: &append_after_point_of_no_return/2

  def arguments(%Edeliver.Relup.Instructions{up_version: up_version}, %{name: name}) do
    {name, up_version}
  end

  def run({otp_name, up_version}) do
    info "Running pending migrations..."
    {:ok, [ecto_repository_module]} = Application.fetch_env(otp_name, :ecto_repos) # requires ecto 2.0
    Ecto.Migrator.run(ecto_repository_module, migrations_dir(otp_name, up_version), :up, [all: true])
  end

  defp migrations_dir(application_name, application_version) do
    # use priv dir from the new version
    lib_dir = :code.priv_dir(application_name) |> to_string |> Path.dirname |> Path.dirname
    application_with_version = "#{Atom.to_string(application_name)}-#{application_version}"
    Path.join([lib_dir, application_with_version, "priv", "repo", "migrations"])
  end
end

With all of that, migrations are now happening automatically every time I deploy.

Hey @dmarkov,

thanks for the PR and for stepping in. It’s all good after it was merged. I suspect it was updated function signature that made difference.
Now everything is back on track again :slight_smile: