0
votes

I have an umbrella application in elixir with two app endpoints, one for API, and another one for IoT. I am using a reverse proxy to serve the application as there are two endpoints running on different ports.

The build pack being used for the reverse proxy is: https://github.com/heroku/heroku-buildpack-nginx

This is my Nginx configuration file.

daemon off;
worker_processes <%= ENV['NGINX_WORKERS'] || 4 %>;

events {
 use epoll;
 accept_mutex on;
 worker_connections <%= ENV['NGINX_WORKER_CONNECTIONS'] || 1024 %>;
}

http {
    gzip on;
    gzip_comp_level 2;
    gzip_min_length 512;

server_tokens off;

log_format l2met 'measure#nginx.service=$request_time request_id=$http_x_request_id';
access_log <%= ENV['NGINX_ACCESS_LOG_PATH'] || 'logs/nginx/access.log' %> l2met;
error_log <%= ENV['NGINX_ERROR_LOG_PATH'] || 'logs/nginx/error.log' %>;

include mime.types;
default_type application/octet-stream;
sendfile on;

#Must read the body in 5 seconds.
client_body_timeout 5;

upstream api {
    server 127.0.0.1:4000 max_fails=5 fail_timeout=60s;
}

upstream iot {
    server 127.0.0.1:4001 max_fails=5 fail_timeout=60s;
}

server {
    listen <%= ENV["PORT"] %>;
    server_name 0.0.0.0;
    keepalive_timeout 5;

    location /api {
        allow all;

        # Proxy Headers
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-Cluster-Client-Ip $remote_addr;

        # The Important Websocket Bits!
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_pass http://api;

    }

    location /iot {
        allow all;

        # Proxy Headers
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-Cluster-Client-Ip $remote_addr;

        # The Important Websocket Bits!
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_pass http://iot;

    }
}

}

Apparently, the buildpack expects a file /tmp/app-initialized to be written once the app is initialized.

The command I am using is mix phx.server to start the umbrella. What would be the right way to write this file(/tmp/app-initialized) after app initialization in the phoenix umbrella.

1

1 Answers

1
votes

You can see the phx.server task phx.server.ex

You can slightly change that and create your own task that does the same plus writing the file:

defmodule Mix.Tasks.Bootstrap do
  use Mix.Task

  def run(_) do
    Application.put_env(:phoenix, :serve_endpoints, true, persistent: true)
    case Application.ensure_all_started(:the_name_of_your_app) do
       {:ok, _} ->
            ####
            #### write the file
            ####
            Mix.Tasks.Run.run run_args() ++ ["--no-start"]
       _error -> raise "Unable to start..."
    end
  end

  defp run_args do
    if iex_running?(), do: [], else: ["--no-halt"]
  end

  defp iex_running? do
    Code.ensure_loaded?(IEx) and IEx.started?
  end

end

You just need to drop this new file into somewhere in lib and then pass the command as mix bootstrap instead of mix phx.server.

Given how applications & (?) components (?) are started in erlang&elixir, you could also have a simple gen_server as part of your application supervision tree, after the endpoints have been initialised, that does that writing.

defmodule Server.Application do

  @moduledoc false

  use Application

  def start(_type, _args) do
    children = [
      ServerWeb.Endpoint,
      ServerIOT.Endpoint,
      Supervisor.child_spec(%{id: Server.Liveness, start: {Server.Liveness, :start_link, []}}, type: :worker)
    ]

    opts = [strategy: :one_for_one, name: Server.Supervisor]
    Supervisor.start_link(children, opts)
  end

  # Tell Phoenix to update the endpoint configuration
  # whenever the application is updated.
  def config_change(changed, _new, removed) do
    ServerWeb.Endpoint.config_change(changed, removed)
    :ok
  end
end

Obviously changing the names for your case. The Server.Liveness could simply be a transient gen_server, that only does that, write the file on start and exiting normally afterwards.