From 96ce16056348a586912bfdf120c13924002c28f4 Mon Sep 17 00:00:00 2001 From: Louis G Date: Mon, 31 Jan 2022 09:59:08 +0100 Subject: [PATCH] feat!: Widget system (#27) * WIP * WIP * Add some logs + some fix * fix tests and fix dialyzer * change entrypoint to rootWidget * change application_runner to beta-17 * Config Lenra-CI branch * woops * Remove some old commented code Co-authored-by: Louis G --- .../lib/lenra/services/datastore_services.ex | 11 +- .../lib/lenra/services/deployment_services.ex | 4 +- .../lib/lenra/services/gitlab_api_services.ex | 3 +- .../{openfaas.ex => openfaas_services.ex} | 104 ++++++++------ .../lib/lenra/services/resources_services.ex | 4 +- .../services/datastore_services_test.exs | 35 ++--- ...as_test.exs => openfaas_services_test.exs} | 43 +++--- apps/lenra/test/support/faas_stub_helper.ex | 11 +- .../lenra_web/application_runner_adapter.ex | 100 ++++++++++--- .../lib/lenra_web/channels/app_channel.ex | 136 ++++++++---------- apps/lenra_web/mix.exs | 4 +- .../test/channels/app_channel_test.exs | 92 +++++------- apps/lenra_web/test/test_helper.exs | 3 +- config/config.exs | 9 +- config/dev.exs | 11 +- config/releases.exs | 3 +- config/test.exs | 3 +- mix.lock | 5 +- 18 files changed, 312 insertions(+), 269 deletions(-) rename apps/lenra/lib/lenra/services/{openfaas.ex => openfaas_services.ex} (57%) rename apps/lenra/test/lenra/services/{app_services/openfaas_test.exs => openfaas_services_test.exs} (66%) diff --git a/apps/lenra/lib/lenra/services/datastore_services.ex b/apps/lenra/lib/lenra/services/datastore_services.ex index bbcc4349..09c32cb0 100644 --- a/apps/lenra/lib/lenra/services/datastore_services.ex +++ b/apps/lenra/lib/lenra/services/datastore_services.ex @@ -19,14 +19,11 @@ defmodule Lenra.DatastoreServices do @doc """ Gets the datastore data - Returns `%{:ok, action}` with :old_data nil if the datastore does not exist. - Returns a `%{:ok, action}` with data assign to :old_data if the datastore exists. + Returns the data. + data is nil if the data does not exists for this user/app """ - def assign_old_data(action, application_id) do - case get_by(user_id: action.user_id, application_id: application_id) do - nil -> {:ok, action} - datastore -> {:ok, %{action | old_data: datastore.data}} - end + def get_old_data(user_id, application_id) do + get_by(user_id: user_id, application_id: application_id) end @doc """ diff --git a/apps/lenra/lib/lenra/services/deployment_services.ex b/apps/lenra/lib/lenra/services/deployment_services.ex index 5a10c727..50f16e6e 100644 --- a/apps/lenra/lib/lenra/services/deployment_services.ex +++ b/apps/lenra/lib/lenra/services/deployment_services.ex @@ -4,7 +4,7 @@ defmodule Lenra.DeploymentServices do """ require Logger - alias Lenra.{Repo, Deployment, Build, BuildServices, EnvironmentServices, Openfaas} + alias Lenra.{Repo, Deployment, Build, BuildServices, EnvironmentServices, OpenfaasServices} def get(deployment_id) do Repo.get(Deployment, deployment_id) @@ -38,7 +38,7 @@ defmodule Lenra.DeploymentServices do ) |> Ecto.Multi.run(:openfaas_deploy, fn _repo, _ -> # a faire: check if this build is already deployed on another env - Openfaas.deploy_app(build.application.service_name, build.build_number) + OpenfaasServices.deploy_app(build.application.service_name, build.build_number) end) |> Repo.transaction() end diff --git a/apps/lenra/lib/lenra/services/gitlab_api_services.ex b/apps/lenra/lib/lenra/services/gitlab_api_services.ex index eedda0e2..df78f300 100644 --- a/apps/lenra/lib/lenra/services/gitlab_api_services.ex +++ b/apps/lenra/lib/lenra/services/gitlab_api_services.ex @@ -21,6 +21,7 @@ defmodule Lenra.GitlabApiServices do gitlab_api_token = Application.fetch_env!(:lenra, :gitlab_api_token) gitlab_project_id = Application.fetch_env!(:lenra, :gitlab_project_id) runner_secret = Application.fetch_env!(:lenra, :runner_secret) + gitlab_ref = Application.fetch_env!(:lenra, :gitlab_ci_ref) url = "#{gitlab_api_url}/projects/#{gitlab_project_id}/pipeline" @@ -31,7 +32,7 @@ defmodule Lenra.GitlabApiServices do body = Jason.encode!(%{ - "ref" => "master", + "ref" => gitlab_ref, "variables" => [ %{"key" => "IMAGE_NAME", "value" => DeploymentServices.image_name(service_name, build_number)}, %{ diff --git a/apps/lenra/lib/lenra/services/openfaas.ex b/apps/lenra/lib/lenra/services/openfaas_services.ex similarity index 57% rename from apps/lenra/lib/lenra/services/openfaas.ex rename to apps/lenra/lib/lenra/services/openfaas_services.ex index cbbcef75..c51a2dfc 100644 --- a/apps/lenra/lib/lenra/services/openfaas.ex +++ b/apps/lenra/lib/lenra/services/openfaas_services.ex @@ -1,11 +1,10 @@ -defmodule Lenra.Openfaas do +defmodule Lenra.OpenfaasServices do @moduledoc """ The service that manage calls to an Openfaas action with `run_action/3` """ require Logger - alias Lenra.{Telemetry, DeploymentServices} - alias ApplicationRunner.Action + alias Lenra.{DeploymentServices, Environment, LenraApplication} defp get_http_context do base_url = Application.fetch_env!(:lenra, :faas_url) @@ -17,7 +16,9 @@ defmodule Lenra.Openfaas do defp get_function_name(service_name, build_number) do lenra_env = Application.fetch_env!(:lenra, :lenra_env) + "#{lenra_env}-#{service_name}-#{build_number}" + |> String.downcase() end @doc """ @@ -26,50 +27,70 @@ defmodule Lenra.Openfaas do Returns `{:ok, decoded_body}` if the HTTP Post succeed Returns `{:error, reason}` if the HTTP Post fail """ - @spec run_action(Action.t()) :: {:ok, map} - def run_action(action) - when is_binary(action.app_name) and is_binary(action.action_name) and - is_map(%{data: action.old_data, props: action.props, event: action.event}) do + @spec run_listener(%LenraApplication{}, %Environment{}, String.t(), map(), map(), map()) :: + {:ok, map()} | {:error, any()} + def run_listener(%LenraApplication{} = application, %Environment{} = environment, action, data, props, event) do {base_url, headers} = get_http_context() - function_name = get_function_name(action.app_name, action.build_number) + function_name = get_function_name(application.service_name, environment.deployed_build.build_number) url = "#{base_url}/function/#{function_name}" - Logger.debug("Call to Openfaas : #{function_name}") - headers = [{"Content-Type", "application/json"} | headers] - params = Map.put(%{data: action.old_data, props: action.props, event: action.event}, :action, action.action_name) - body = Jason.encode!(params) + body = Jason.encode!(%{action: action, data: data, props: props, event: event}) - Logger.info("Run app #{action.app_name}[#{action.build_number}] with action #{action.action_name}") + Logger.debug("Call to Openfaas : #{function_name}") - start_time = Telemetry.start(:openfaas_runaction) + Logger.debug( + "Run app #{application.service_name}[#{environment.deployed_build.build_number}] with action #{action}" + ) - response = - Finch.build(:post, url, headers, body) - |> Finch.request(FaasHttp) - |> response(:get_apps) + Finch.build(:post, url, headers, body) + |> Finch.request(FaasHttp) + |> response(:decode) + |> case do + {:ok, %{"data" => data}} -> {:ok, data} + err -> err + end + end - docker_telemetry(response, action.action_logs_uuid) + @spec fetch_widget(%LenraApplication{}, %Environment{}, String.t(), map(), map()) :: {:ok, map()} | {:error, any()} + def fetch_widget(%LenraApplication{} = application, %Environment{} = environment, widget_name, data, props) do + {base_url, headers} = get_http_context() + function_name = get_function_name(application.service_name, environment.deployed_build.build_number) - Telemetry.stop(:openfaas_runaction, start_time, %{ - user_id: action.user_id, - uuid: action.action_logs_uuid - }) + url = "#{base_url}/function/#{function_name}" + headers = [{"Content-Type", "application/json"} | headers] + body = Jason.encode!(%{widget: widget_name, data: data, props: props}) - response + Finch.build(:post, url, headers, body) + |> Finch.request(FaasHttp) + |> response(:decode) + |> case do + {:ok, %{"widget" => widget}} -> {:ok, widget} + err -> err + end end - defp docker_telemetry({:ok, %{"stats" => %{"listeners" => listeners, "ui" => ui}}}, uuid) do - Telemetry.event(:docker_run, %{uuid: uuid}, %{ - uiDuration: ui, - listenersTime: listeners - }) - end + @spec fetch_manifest(%LenraApplication{}, %Environment{}) :: {:ok, map()} | {:error, any()} + def fetch_manifest(%LenraApplication{} = application, %Environment{} = environment) do + {base_url, headers} = get_http_context() + function_name = get_function_name(application.service_name, environment.deployed_build.build_number) - defp docker_telemetry(_response, _uuid) do - # credo:disable-for-next-line - # TODO: manage error case + url = "#{base_url}/function/#{function_name}" + headers = [{"Content-Type", "application/json"} | headers] + + Finch.build(:post, url, headers) + |> Finch.request(FaasHttp) + |> response(:decode) + |> case do + {:ok, %{"manifest" => manifest}} -> + Logger.debug("Got manifest : #{inspect(manifest)}") + {:ok, manifest} + + err -> + Logger.error("Error while getting manifest : #{inspect(err)}") + err + end end @doc """ @@ -96,19 +117,22 @@ defmodule Lenra.Openfaas do def deploy_app(service_name, build_number) do {base_url, headers} = get_http_context() - Logger.debug("Deploy Openfaas application") - url = "#{base_url}/system/functions" - Finch.build( - :post, - url, - headers, + body = Jason.encode!(%{ "image" => DeploymentServices.image_name(service_name, build_number), "service" => get_function_name(service_name, build_number), "secrets" => Application.fetch_env!(:lenra, :faas_secrets) }) + + Logger.debug("Deploy Openfaas application \n#{url} : \n#{body}") + + Finch.build( + :post, + url, + headers, + body ) |> Finch.request(FaasHttp) |> response(:deploy_app) @@ -133,7 +157,7 @@ defmodule Lenra.Openfaas do |> response(:delete_app) end - defp response({:ok, %Finch.Response{status: 200, body: body}}, :get_apps) do + defp response({:ok, %Finch.Response{status: 200, body: body}}, :decode) do {:ok, Jason.decode!(body)} end diff --git a/apps/lenra/lib/lenra/services/resources_services.ex b/apps/lenra/lib/lenra/services/resources_services.ex index e52c3992..7fe951fb 100644 --- a/apps/lenra/lib/lenra/services/resources_services.ex +++ b/apps/lenra/lib/lenra/services/resources_services.ex @@ -4,7 +4,7 @@ defmodule Lenra.ResourcesServices do """ require Logger - alias Lenra.{Openfaas, LenraApplicationServices, Repo} + alias Lenra.{OpenfaasServices, LenraApplicationServices, Repo} @doc """ Gets the `resource` from an app. @@ -15,7 +15,7 @@ defmodule Lenra.ResourcesServices do with {:ok, app} <- LenraApplicationServices.fetch_by(service_name: service_name, creator_id: user_id), loaded_app <- Repo.preload(app, main_env: [environment: [:deployed_build]]) do build_number = loaded_app.main_env.environment.deployed_build.build_number - Openfaas.get_app_resource(app.service_name, build_number, resource) + OpenfaasServices.get_app_resource(app.service_name, build_number, resource) end end end diff --git a/apps/lenra/test/lenra/services/datastore_services_test.exs b/apps/lenra/test/lenra/services/datastore_services_test.exs index ec4f3e14..b4c5096e 100644 --- a/apps/lenra/test/lenra/services/datastore_services_test.exs +++ b/apps/lenra/test/lenra/services/datastore_services_test.exs @@ -26,48 +26,31 @@ defmodule LenraServers.DatastoreServicesTest do describe "get" do test "data from datastore but datastore does not exist", %{app: app} do - assert {:ok, %{}} = - DatastoreServices.assign_old_data( - %ApplicationRunner.Action{ - app_name: "test", - build_number: 42, - action_logs_uuid: "92846b3e-e85a-431d-9138-8b7ddaf05f66", - user_id: app.creator_id - }, + assert nil == + DatastoreServices.get_old_data( + app.creator_id, app.id ) end test "data from existing datastore", %{app: app} do - DatastoreServices.upsert_data(app.creator_id, app.id, %{"data" => "test data"}) + DatastoreServices.upsert_data(app.creator_id, app.id, %{"foo" => "bar"}) - assert {:ok, - %ApplicationRunner.Action{ - app_name: "test", - build_number: 42, - action_logs_uuid: "92846b3e-e85a-431d-9138-8b7ddaf05f66", - old_data: %{"data" => "test data"}, - user_id: app.creator_id - }} == - DatastoreServices.assign_old_data( - %ApplicationRunner.Action{ - app_name: "test", - build_number: 42, - action_logs_uuid: "92846b3e-e85a-431d-9138-8b7ddaf05f66", - user_id: app.creator_id - }, + assert %Datastore{data: %{"foo" => "bar"}} = + DatastoreServices.get_old_data( + app.creator_id, app.id ) end test "datastore", %{app: app} do - DatastoreServices.upsert_data(app.creator_id, app.id, %{"data" => "test data"}) + DatastoreServices.upsert_data(app.creator_id, app.id, %{"foo" => "bar"}) assert (%Datastore{} = datastore) = DatastoreServices.get_by(user_id: app.creator_id, application_id: app.id) assert datastore.user_id == app.creator_id assert datastore.application_id == app.id - assert datastore.data == %{"data" => "test data"} + assert datastore.data == %{"foo" => "bar"} end test "datastore but does not exist", %{app: app} do diff --git a/apps/lenra/test/lenra/services/app_services/openfaas_test.exs b/apps/lenra/test/lenra/services/openfaas_services_test.exs similarity index 66% rename from apps/lenra/test/lenra/services/app_services/openfaas_test.exs rename to apps/lenra/test/lenra/services/openfaas_services_test.exs index b52625ab..27ca3c97 100644 --- a/apps/lenra/test/lenra/services/app_services/openfaas_test.exs +++ b/apps/lenra/test/lenra/services/openfaas_services_test.exs @@ -1,4 +1,4 @@ -defmodule LenraServers.OpenfaasTest do +defmodule LenraServers.OpenfaasServicesTest do @moduledoc """ Test the Errors for some routes """ @@ -6,10 +6,10 @@ defmodule LenraServers.OpenfaasTest do use Lenra.RepoCase alias Lenra.FaasStub, as: AppStub - alias Lenra.{Openfaas, LenraApplication, Build} + alias Lenra.{OpenfaasServices, LenraApplication, Build, Environment} @john_doe_application %LenraApplication{ - name: "test", + name: "stubapp", service_name: "test_service", color: "FFFFFF", icon: 1 @@ -22,34 +22,29 @@ defmodule LenraServers.OpenfaasTest do application: @john_doe_application } - @john_doe_action %ApplicationRunner.Action{ - user_id: 1, - app_name: "StubApp", - build_number: 1, - action_name: "InitData", - old_data: %{"toto" => "tata"}, - action_logs_uuid: "92846b3e-e85a-431d-9138-8b7ddaf05f66" + @john_doe_environment %Environment{ + deployed_build: @john_doe_build } - describe "applist" do + describe "run_listener" do setup do faas = AppStub.create_faas_stub() - app = AppStub.stub_app(faas, "StubApp", 1) + app = AppStub.stub_app(faas, @john_doe_application.service_name, 1) {:ok, %{app: app, faas: faas}} end test "Openfaas correctly handle ok 200 and decode data", %{app: app} do - AppStub.stub_action_once(app, "InitData", %{"stats" => %{"listeners" => 111_111, "ui" => 111_111}}) + AppStub.stub_action_once(app, "InitData", %{"data" => %{"foo" => "bar"}}) - assert {:ok, %{"stats" => %{"listeners" => 111_111, "ui" => 111_111}}} == - Openfaas.run_action(@john_doe_action) + assert {:ok, %{"foo" => "bar"}} == + OpenfaasServices.run_listener(@john_doe_application, @john_doe_environment, "InitData", %{}, %{}, %{}) end test "Openfaas correctly handle 404 not found", %{app: app} do AppStub.stub_action_once(app, "InitData", {:error, 404, "Not Found"}) assert_raise(RuntimeError, "Openfaas error (404) Not Found", fn -> - Openfaas.run_action(@john_doe_action) + OpenfaasServices.run_listener(@john_doe_application, @john_doe_environment, "InitData", %{}, %{}, %{}) end) end end @@ -57,16 +52,16 @@ defmodule LenraServers.OpenfaasTest do describe "get_app_resource" do test "openfaas not accessible" do assert {:error, %Mint.TransportError{reason: :econnrefused}} == - Openfaas.get_app_resource("test", 1, "download.jpeg") + OpenfaasServices.get_app_resource("invalid", 1, "download.jpeg") end test "successful for very small data" do faas = AppStub.create_faas_stub() - app = AppStub.stub_app_resource(faas, "StubApp", 1) + app = AppStub.stub_app_resource(faas, @john_doe_application.service_name, 1) AppStub.stub_resource_once(app, "download.jpeg", %{}) - {:ok, res} = Openfaas.get_app_resource("StubApp", 1, "download.jpeg") + {:ok, res} = OpenfaasServices.get_app_resource(@john_doe_application.service_name, 1, "download.jpeg") assert Keyword.get(res, :data) == "{}" end @@ -75,7 +70,7 @@ defmodule LenraServers.OpenfaasTest do describe "deploy" do test "app but openfaas unreachable" do assert_raise(RuntimeError, "Openfaas could not be reached. It should not happen.", fn -> - Openfaas.deploy_app(@john_doe_application.service_name, @john_doe_build.build_number) + OpenfaasServices.deploy_app(@john_doe_application.service_name, @john_doe_build.build_number) end) end @@ -83,7 +78,7 @@ defmodule LenraServers.OpenfaasTest do AppStub.create_faas_stub() |> AppStub.expect_deploy_app_once(%{"ok" => "200"}) - res = Openfaas.deploy_app(@john_doe_application.service_name, @john_doe_build.build_number) + res = OpenfaasServices.deploy_app(@john_doe_application.service_name, @john_doe_build.build_number) assert res == {:ok, 200} end @@ -95,7 +90,7 @@ defmodule LenraServers.OpenfaasTest do |> AppStub.expect_delete_app_once(%{"ok" => "200"}) res = - Openfaas.delete_app_openfaas( + OpenfaasServices.delete_app_openfaas( @john_doe_application.service_name, @john_doe_build.build_number ) @@ -111,7 +106,7 @@ defmodule LenraServers.OpenfaasTest do RuntimeError, "Openfaas could not delete the application. It should not happen.", fn -> - Openfaas.delete_app_openfaas( + OpenfaasServices.delete_app_openfaas( @john_doe_application.service_name, @john_doe_build.build_number ) @@ -125,7 +120,7 @@ defmodule LenraServers.OpenfaasTest do |> AppStub.expect_delete_app_once({:error, 404, "Not found"}) res = - Openfaas.delete_app_openfaas( + OpenfaasServices.delete_app_openfaas( @john_doe_application.service_name, @john_doe_build.build_number ) diff --git a/apps/lenra/test/support/faas_stub_helper.ex b/apps/lenra/test/support/faas_stub_helper.ex index 87affa99..2de49ed9 100644 --- a/apps/lenra/test/support/faas_stub_helper.ex +++ b/apps/lenra/test/support/faas_stub_helper.ex @@ -65,12 +65,13 @@ defmodule Lenra.FaasStub do app end - defp handle_action(conn, app_name) do - {:ok, body, _} = Plug.Conn.read_body(conn) - %{"action" => action_code} = Jason.decode!(body) + def stub_request_once({_bypass, app_name} = app, result) do + push(app_name, {nil, result}) + app + end - {stored_action_code, result} = pop(app_name) - assert stored_action_code == action_code + defp handle_action(conn, app_name) do + {_stored_action_code, result} = pop(app_name) case result do {:error, code, message} -> diff --git a/apps/lenra_web/lib/lenra_web/application_runner_adapter.ex b/apps/lenra_web/lib/lenra_web/application_runner_adapter.ex index 727477cb..7266b82d 100644 --- a/apps/lenra_web/lib/lenra_web/application_runner_adapter.ex +++ b/apps/lenra_web/lib/lenra_web/application_runner_adapter.ex @@ -5,32 +5,98 @@ defmodule LenraWeb.ApplicationRunnerAdapter do """ @behaviour ApplicationRunner.AdapterBehavior - alias Lenra.{LenraApplicationServices, DatastoreServices, Openfaas} + require require Logger + + alias Lenra.{DatastoreServices, OpenfaasServices, User, LenraApplication, Datastore} + alias ApplicationRunner.{EnvState, SessionState} + + @impl true + def run_listener( + %EnvState{ + assigns: %{ + environment: environment, + application: application + } + }, + action, + data, + props, + event + ) do + Logger.info("Run listener for action #{action}") + OpenfaasServices.run_listener(application, environment, action, data, props, event) + end @impl true - def run_action(action) do - Openfaas.run_action(action) + def get_widget( + %EnvState{ + assigns: %{ + environment: environment, + application: application + } + }, + widget_name, + data, + props + ) do + Logger.info("Get widget #{widget_name}") + OpenfaasServices.fetch_widget(application, environment, widget_name, data, props) end @impl true - def get_data(action) do - with {:ok, application} <- - LenraApplicationServices.fetch_by( - [service_name: action.app_name], - {:error, :no_such_application} - ) do - DatastoreServices.assign_old_data(action, application.id) + def get_manifest(%EnvState{ + assigns: %{ + environment: environment, + application: application + } + }) do + Logger.info("Get manifest") + + OpenfaasServices.fetch_manifest(application, environment) + end + + def get_manifest(env) do + raise "Woops, env not good #{inspect(env)}" + end + + @impl true + def get_data(%SessionState{ + assigns: %{ + application: %LenraApplication{} = application, + user: %User{} = user + } + }) do + case DatastoreServices.get_old_data(user.id, application.id) do + nil -> {:ok, %{}} + %Datastore{} = datastore -> {:ok, datastore.data} end end @impl true - def save_data(action, data) do - with {:ok, application} <- - LenraApplicationServices.fetch_by( - [service_name: action.app_name], - {:error, :no_such_application} - ) do - DatastoreServices.upsert_data(action.user_id, application.id, data) + def save_data( + %SessionState{ + assigns: %{ + application: %LenraApplication{} = application, + user: %User{} = user + } + }, + data + ) do + case DatastoreServices.upsert_data(user.id, application.id, data) do + {:ok, _} -> :ok + {:error, _} -> {:error, :cannot_save_data} end end + + @impl true + def on_ui_changed( + %SessionState{ + assigns: %{ + socket_pid: socket_pid + } + }, + {atom, ui_or_patches} + ) do + send(socket_pid, {:send, atom, ui_or_patches}) + end end diff --git a/apps/lenra_web/lib/lenra_web/channels/app_channel.ex b/apps/lenra_web/lib/lenra_web/channels/app_channel.ex index 97b1eefb..c6ec0afe 100644 --- a/apps/lenra_web/lib/lenra_web/channels/app_channel.ex +++ b/apps/lenra_web/lib/lenra_web/channels/app_channel.ex @@ -6,58 +6,49 @@ defmodule LenraWeb.AppChannel do require Logger - alias Lenra.{Repo, LenraApplicationServices, Telemetry} - alias ApplicationRunner.{ActionBuilder, Action} + alias Lenra.{Repo, LenraApplicationServices, LenraApplication, Environment} + alias ApplicationRunner.{SessionManagers, SessionManager} def join("app", %{"app" => app_name}, socket) do - action_logs_uuid = Ecto.UUID.generate() - app_user_session_uuid = Ecto.UUID.generate() + # action_logs_uuid = Ecto.UUID.generate() + session_id = Ecto.UUID.generate() + user = socket.assigns.user - Logger.info("Join channel for app : #{app_name}") + Logger.debug("Joining channel for app : #{app_name}") with {:ok, app} <- LenraApplicationServices.fetch_by( service_name: app_name, - creator_id: socket.assigns.user.id + # This restrict to "owner" app only + creator_id: user.id ), - loaded_app <- Repo.preload(app, main_env: [environment: [:deployed_build]]) do - build_number = loaded_app.main_env.environment.deployed_build.build_number - - AppChannelMonitor.monitor(self(), %{ - user_id: socket.assigns.user.id, - app_user_session_uuid: app_user_session_uuid, - app_name: app_name, - build_number: build_number - }) - - socket = - assign(socket, - app_name: app_name, - build_number: build_number, - app_user_session_uuid: app_user_session_uuid, - action_logs_uuid: action_logs_uuid - ) - - Telemetry.event(:action_logs, %{ - uuid: action_logs_uuid, - app_user_session_uuid: app_user_session_uuid, - action: "InitData" - }) - - case ActionBuilder.first_run(%Action{ - action_logs_uuid: action_logs_uuid, - user_id: socket.assigns.user.id, - app_name: app_name, - build_number: build_number - }) do - {:ok, ui} -> - send(self(), {:send_ui, ui}) - + %LenraApplication{} = application <- + Repo.preload(app, main_env: [environment: [:deployed_build]]) do + %Environment{} = environment = select_env(application) + + Logger.debug("Environment selected is #{environment.name}") + + # Assign the session_id to the socket for future usage + socket = assign(socket, session_id: session_id) + + # prepare the assigns to the session/environment + session_assigns = %{ + user: user, + application: application, + environment: environment, + socket_pid: self() + } + + env_assigns = %{application: application, environment: environment} + + with {:ok, session_pid} <- + start_session(environment.id, session_id, session_assigns, env_assigns), + :ok <- SessionManager.init_data(session_pid) do + {:ok, assign(socket, session_pid: session_pid)} + else {:error, reason} -> - Logger.error(inspect(reason)) + {:error, %{reason: reason}} end - - {:ok, socket} else _err -> {:error, %{reason: "No app found"}} end @@ -67,49 +58,46 @@ defmodule LenraWeb.AppChannel do {:error, %{reason: "No App Name"}} end - def handle_info({:send_ui, ui}, socket) do + defp select_env(%LenraApplication{} = app) do + app.main_env.environment + end + + defp start_session(env_id, session_id, session_assigns, env_assigns) do + case SessionManagers.start_session(session_id, env_id, session_assigns, env_assigns) do + {:ok, session_pid} -> {:ok, session_pid} + {:error, {:already_started, session_pid}} -> {:ok, session_pid} + {:error, message} -> {:error, message} + end + end + + def handle_info({:send, :ui, ui}, socket) do + Logger.debug("send ui #{inspect(ui)}") push(socket, "ui", ui) {:noreply, socket} end - def handle_in("run", %{"code" => action_key, "event" => event}, socket) do - handle_run(socket, action_key, event) + def handle_info({:send, :patches, patches}, socket) do + Logger.debug("send patchUi #{inspect(%{patch: patches})}") + + push(socket, "patchUi", %{"patch" => patches}) + {:noreply, socket} + end + + def handle_in("run", %{"code" => code, "event" => event}, socket) do + handle_run(socket, code, event) end - def handle_in("run", %{"code" => action_key}, socket) do - handle_run(socket, action_key) + def handle_in("run", %{"code" => code}, socket) do + handle_run(socket, code) end - defp handle_run(socket, action_key, event \\ %{}) do + defp handle_run(socket, code, event \\ %{}) do %{ - app_name: app_name, - user: user, - build_number: build_number, - app_user_session_uuid: app_user_session_uuid + session_pid: session_pid } = socket.assigns - uuid = Ecto.UUID.generate() - - Telemetry.event(:action_logs, %{ - uuid: uuid, - app_user_session_uuid: app_user_session_uuid, - action: action_key - }) - - case ApplicationRunner.ActionBuilder.listener_run(%Action{ - action_logs_uuid: uuid, - user_id: user.id, - app_name: app_name, - build_number: build_number, - action_key: action_key, - event: event - }) do - {:ok, patch} -> - push(socket, "patchUi", %{patch: patch}) - - {:error, reason} -> - Logger.error(reason) - end + Logger.debug("Handle run #{code}") + SessionManager.run_listener(session_pid, code, event) {:noreply, socket} end diff --git a/apps/lenra_web/mix.exs b/apps/lenra_web/mix.exs index f31442f2..ce06cd60 100644 --- a/apps/lenra_web/mix.exs +++ b/apps/lenra_web/mix.exs @@ -54,8 +54,8 @@ defmodule LenraWeb.MixProject do name: :application_runner, host: "github.com", project: "lenra-io/application-runner.git", - tag: "v1.0.0-beta.13", - credentials: "nesqwik:#{System.get_env("GH_PERSONNAL_TOKEN")}" + tag: "v1.0.0-beta.17", + credentials: "shiipou:#{System.get_env("GH_PERSONNAL_TOKEN")}" ) ] end diff --git a/apps/lenra_web/test/channels/app_channel_test.exs b/apps/lenra_web/test/channels/app_channel_test.exs index 15c0c8f1..c1edd096 100644 --- a/apps/lenra_web/test/channels/app_channel_test.exs +++ b/apps/lenra_web/test/channels/app_channel_test.exs @@ -2,7 +2,7 @@ defmodule LenraWeb.AppChannelTest do @moduledoc """ Test the `LenraWeb.AppChannel` module """ - use LenraWeb.ChannelCase + use LenraWeb.ChannelCase, async: false alias LenraWeb.UserSocket alias Lenra.FaasStub, as: AppStub @@ -12,33 +12,20 @@ defmodule LenraWeb.AppChannelTest do Build, Environment, ApplicationMainEnv, - Deployment, - ActionLogsService, - AppUserSessionService + Deployment } - setup do - {:ok, %{inserted_user: user}} = register_john_doe() - socket = socket(UserSocket, "socket_id", %{user: user}) - - owstub = AppStub.create_faas_stub() - - %{socket: socket, owstub: owstub, user: user} - end - - test "No app called, should return an error", %{socket: socket} do - res = my_subscribe_and_join(socket) - assert {:error, %{reason: "No App Name"}} == res - refute_push("ui", _) - end + alias ApplicationRunner.ListenersCache - @app_name "Counter" + @service_name "hello-world" @build_number 1 @listener_name "HiBob" - @listener_code "#{@listener_name}:{}" + @listener_code ListenersCache.generate_listeners_key(@listener_name, %{}) + + @manifest %{"manifest" => %{"rootWidget" => "test"}} - @data %{"user" => %{"name" => "World"}} - @data2 %{"user" => %{"name" => "Bob"}} + @data %{"data" => %{"user" => %{"name" => "World"}}} + @data2 %{"data" => %{"user" => %{"name" => "Bob"}}} @textfield %{ "type" => "textfield", @@ -58,14 +45,31 @@ defmodule LenraWeb.AppChannelTest do "onChanged" => %{"code" => @listener_code} } - @ui %{"root" => %{"type" => "flex", "children" => [@textfield]}} - @ui2 %{"root" => %{"type" => "flex", "children" => [@textfield2]}} + @widget %{"widget" => %{"type" => "flex", "children" => [@textfield]}} + @widget2 %{"widget" => %{"type" => "flex", "children" => [@textfield2]}} @expected_ui %{"root" => %{"type" => "flex", "children" => [@transformed_textfield]}} @expected_patch_ui %{ - patch: [%{"op" => "replace", "path" => "/root/children/0/value", "value" => "Hello Bob"}] + "patch" => [%{"op" => "replace", "path" => "/root/children/0/value", "value" => "Hello Bob"}] } + setup do + {:ok, %{inserted_user: user}} = register_john_doe() + socket = socket(UserSocket, "socket_id", %{user: user}) + + owstub = + AppStub.create_faas_stub() + |> AppStub.stub_app(@service_name, @build_number) + + %{socket: socket, owstub: owstub, user: user} + end + + test "No app called, should return an error", %{socket: socket} do + res = my_subscribe_and_join(socket) + assert {:error, %{reason: "No App Name"}} == res + refute_push("ui", _) + end + test "Base use case with simple app", %{socket: socket, owstub: owstub, user: user} do # owstub # |> AppStub.expect_deploy_app_once(%{"ok" => "200"}) @@ -75,7 +79,7 @@ defmodule LenraWeb.AppChannelTest do :inserted_application, LenraApplication.new(user.id, %{ name: "Counter", - service_name: @app_name, + service_name: @service_name, color: "FFFFFF", icon: "60189" }) @@ -100,37 +104,19 @@ defmodule LenraWeb.AppChannelTest do # Base use case. Call InitData then MainUI then call the listener # and the next MainUI should not be called but taken from cache instead owstub - |> AppStub.stub_app(@app_name, @build_number) - |> AppStub.stub_action_once("InitData", %{ - "data" => @data, - "ui" => @ui, - "stats" => %{"listeners" => 111_111, "ui" => 111_111} - }) - |> AppStub.stub_action_once(@listener_name, %{ - "data" => @data2, - "ui" => @ui2, - "stats" => %{"listeners" => 111_111, "ui" => 111_111} - }) + |> AppStub.stub_request_once(@manifest) + |> AppStub.stub_request_once(@data) + |> AppStub.stub_request_once(@widget) + |> AppStub.stub_request_once(@data2) + |> AppStub.stub_request_once(@widget2) # Join the channel - {:ok, _, socket} = my_subscribe_and_join(socket, %{"app" => @app_name}) - - # check that action_logs_uuid & app_user_session_uuid are valid - action_logs = ActionLogsService.get_by(%{uuid: socket.assigns.action_logs_uuid}) - - assert is_nil(action_logs) == false - - app_user_session = AppUserSessionService.get_by(%{uuid: socket.assigns.app_user_session_uuid}) + {:ok, _, socket} = my_subscribe_and_join(socket, %{"app" => @service_name}) - assert is_nil(app_user_session) == false # Check that the correct data is stored into the socket - assert socket.assigns == %{ - app_name: @app_name, - user: user, - build_number: @build_number, - action_logs_uuid: socket.assigns.action_logs_uuid, - app_user_session_uuid: socket.assigns.app_user_session_uuid - } + assert %{ + user: ^user + } = socket.assigns # Check that we receive a "ui" event with the final UI assert_push("ui", @expected_ui) diff --git a/apps/lenra_web/test/test_helper.exs b/apps/lenra_web/test/test_helper.exs index 0bbe9636..79d47887 100644 --- a/apps/lenra_web/test/test_helper.exs +++ b/apps/lenra_web/test/test_helper.exs @@ -1,7 +1,6 @@ # Start ExUnit (Unit test module for Elixir) ExUnit.start() -# Ask SQL adapter to use sandbox connexion pool for the Lenra Repo -# Ecto.Adapters.SQL.Sandbox.mode(Lenra.Repo, :manual) + # Initialize the App stub (start the app stub Agent) Lenra.FaasStub.init() # Verify that the Bypass app is started before tests diff --git a/config/config.exs b/config/config.exs index 750731d2..cb03df56 100644 --- a/config/config.exs +++ b/config/config.exs @@ -60,14 +60,7 @@ config :ex_component_schema, {ApplicationRunner.JsonSchemata, :read_schema} config :application_runner, - adapter: LenraWeb.ApplicationRunnerAdapter, - app_loader: ApplicationRunner.AppLoaderImpl, - # 10 min - session_inactivity_timeout: 1000 * 60 * 10, - # 60 min - app_inactivity_timeout: 1000 * 60 * 60, - additional_app_modules: [], - additional_session_modules: [] + adapter: LenraWeb.ApplicationRunnerAdapter config :lenra, faas_secrets: [] diff --git a/config/dev.exs b/config/dev.exs index f2e029d3..29199139 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -64,15 +64,20 @@ config :phoenix, :stacktrace_depth, 20 config :phoenix, :plug_init_mode, :runtime config :lenra, - faas_url: System.get_env("FAAS_URL", "http://localhost:8080"), - faas_auth: System.get_env("FAAS_AUTH", "Basic YWRtaW46M2kwREc4NTdLWlVaODQ3R0pheW5qMXAwbQ=="), + faas_url: System.get_env("FAAS_URL", "https://openfaas-dev.lenra.me"), + faas_auth: System.get_env("FAAS_AUTH", "Basic YWRtaW46Z0Q4VjNHR1YxeUpS"), faas_registry: System.get_env("FAAS_REGISTRY", "registry.gitlab.com/lenra/platform/lenra-ci"), runner_callback_url: System.get_env("LOCAL_TUNNEL_URL"), lenra_env: "dev", gitlab_api_url: System.get_env("GITLAB_API_URL", "https://gitlab.com/api/v4"), gitlab_api_token: System.get_env("GITLAB_API_TOKEN", "Zuz-dZc834q3CtU-bnX5"), gitlab_project_id: System.get_env("GITLAB_PROJECT_ID", "26231009"), - runner_secret: System.get_env("RUNNER_SECRET", "sZWshq6h0RNO9T1GgUnzLmPpDkSkDAoukmd30mTuwQAGIHYIIVdl7VD2h305"), + gitlab_ci_ref: System.get_env("GITLAB_CI_REF", "master"), + runner_secret: + System.get_env( + "RUNNER_SECRET", + "sZWshq6h0RNO9T1GgUnzLmPpDkSkDAoukmd30mTuwQAGIHYIIVdl7VD2h305" + ), faas_secrets: ["gitlab-registry"] config :peerage, diff --git a/config/releases.exs b/config/releases.exs index b9ffb1ac..c466acf7 100644 --- a/config/releases.exs +++ b/config/releases.exs @@ -23,7 +23,8 @@ config :lenra, lenra_env: System.fetch_env!("ENVIRONMENT"), gitlab_api_url: System.fetch_env!("GITLAB_API_URL"), gitlab_api_token: System.fetch_env!("GITLAB_API_TOKEN"), - gitlab_project_id: System.fetch_env!("GITLAB_PROJECT_ID") + gitlab_project_id: System.fetch_env!("GITLAB_PROJECT_ID"), + gitlab_ci_ref: "master" # Do not print debug messages in production config :logger, level: String.to_atom(System.get_env("LOG_LEVEL", "info")) diff --git a/config/test.exs b/config/test.exs index 3474887a..70749533 100644 --- a/config/test.exs +++ b/config/test.exs @@ -18,7 +18,8 @@ config :lenra, gitlab_api_url: "http://localhost:4567", gitlab_api_token: "none", gitlab_project_id: "26231009", - runner_secret: "test_secret" + runner_secret: "test_secret", + gitlab_ci_ref: "master" config :lenra, Lenra.Repo, username: "postgres", diff --git a/mix.lock b/mix.lock index c6119c2d..c3096542 100644 --- a/mix.lock +++ b/mix.lock @@ -1,5 +1,5 @@ %{ - "application_runner": {:git, "git@github.com:lenra-io/application-runner.git", "771c91d4025db70043806d709f4493854f1d4053", [tag: "v1.0.0-beta.13", submodules: true]}, + "application_runner": {:git, "git@github.com:lenra-io/application-runner.git", "8375d360123bd900fb9a712769af1ac5466f2065", [tag: "v1.0.0-beta.17", submodules: true]}, "argon2_elixir": {:hex, :argon2_elixir, "2.4.0", "2a22ea06e979f524c53b42b598fc6ba38cdcbc977a155e33e057732cfb1fb311", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "4ea82e183cf8e7f66dab1f767fedcfe6a195e140357ef2b0423146b72e0a551d"}, "bamboo": {:hex, :bamboo, "2.1.0", "3c58f862efd74fa8c8d48a410ac592b41f7d24785e828566f7a0af549269ddc3", [:mix], [{:hackney, ">= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.4", [hex: :mime, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "f0ad2623b9a1d2dc06dcf289b59df9ebc522f49f3a21971ec87a8fce04e6d33e"}, "bamboo_smtp": {:hex, :bamboo_smtp, "4.0.1", "7e48188663f6164a81183688bb263be4c3952648fcd3ce52164f44d68777f9cd", [:mix], [{:bamboo, "~> 2.1.0", [hex: :bamboo, repo: "hexpm", optional: false]}, {:gen_smtp, "~> 1.1.1", [hex: :gen_smtp, repo: "hexpm", optional: false]}], "hexpm", "7ff1d62ae39bfb1c14f6d3cddba0fa1482a45c2a2b497a2da601eff7099605c8"}, @@ -33,6 +33,7 @@ "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, "finch": {:hex, :finch, "0.8.0", "9fe1b7b1613f4f0f43ac4be94462a0f3eb13264e5e9a624005da5670e452a1d1", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.3.5", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "07650fd74da3fb51984b9f62bf4db3ef720f3c06d914d716cceeb13a2bc2542d"}, "gen_smtp": {:hex, :gen_smtp, "1.1.1", "bf9303c31735100631b1d708d629e4c65944319d1143b5c9952054f4a1311d85", [:rebar3], [{:hut, "1.3.0", [hex: :hut, repo: "hexpm", optional: false]}, {:ranch, ">= 1.7.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "51bc50cc017efd4a4248cbc39ea30fb60efa7d4a49688986fafad84434ff9ab7"}, + "gen_state_machine": {:hex, :gen_state_machine, "2.1.0", "a38b0e53fad812d29ec149f0d354da5d1bc0d7222c3711f3a0bd5aa608b42992", [:mix], [], "hexpm", "ae367038808db25cee2f2c4b8d0531522ea587c4995eb6f96ee73410a60fa06b"}, "guardian": {:hex, :guardian, "2.1.2", "bfa5f472319f227e0e72d156fa5f7e82dc32043934abf9aa86a1eeab2904849d", [:mix], [{:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "76aafc19311e0cc1e9c26bab7707680407a85df7187b9e3ad4e48890e3600954"}, "guardian_db": {:hex, :guardian_db, "2.1.0", "ec95a9d99cdd1e550555d09a7bb4a340d8887aad0697f594590c2fd74be02426", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:guardian, "~> 1.0 or ~> 2.0", [hex: :guardian, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "f8e7d543ac92c395f3a7fd5acbe6829faeade57d688f7562e2f0fca8f94a0d70"}, "hackney": {:hex, :hackney, "1.17.4", "99da4674592504d3fb0cfef0db84c3ba02b4508bae2dff8c0108baa0d6e0977c", [:rebar3], [{:certifi, "~>2.6.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "de16ff4996556c8548d512f4dbe22dd58a587bf3332e7fd362430a7ef3986b16"}, @@ -42,6 +43,7 @@ "jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"}, "jose": {:hex, :jose, "1.11.1", "59da64010c69aad6cde2f5b9248b896b84472e99bd18f246085b7b9fe435dcdb", [:mix, :rebar3], [], "hexpm", "078f6c9fb3cd2f4cfafc972c814261a7d1e8d2b3685c0a76eb87e158efff1ac5"}, "json_diff": {:hex, :json_diff, "0.1.3", "c80d5ca5416e785867e765e906e9a91b7efc35bfd505af276654d108f4995736", [:mix], [], "hexpm", "a5332e8293e7e9f384d34ea44645d7961334db73739165178fd4a7728d06f7d1"}, + "libring": {:hex, :libring, "1.6.0", "d5dca4bcb1765f862ab59f175b403e356dec493f565670e0bacc4b35e109ce0d", [:mix], [], "hexpm", "5e91ece396af4bce99953d49ee0b02f698cd38326d93cd068361038167484319"}, "makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"}, "makeup_elixir": {:hex, :makeup_elixir, "0.15.1", "b5888c880d17d1cc3e598f05cdb5b5a91b7b17ac4eaf5f297cb697663a1094dd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "db68c173234b07ab2a07f645a5acdc117b9f99d69ebf521821d89690ae6c6ec8"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, @@ -70,6 +72,7 @@ "simplehttp": {:hex, :simplehttp, "0.5.1", "103d027c50398b1e2cf26329cd78d8cf55211c17d19e0bb258a7987fe8df3584", [:mix], [], "hexpm", "32a945235f59cdd6615478f143807f79416555559bf0a701971570628a6884f1"}, "sobelow": {:hex, :sobelow, "0.11.1", "23438964486f8112b41e743bbfd402da3e5b296fdc9eacab29914b79c48916dd", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "9897363a7eff96f4809304a90aad819e2ad5e5d24db547af502885146746a53c"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"}, + "swarm": {:hex, :swarm, "3.4.0", "64f8b30055d74640d2186c66354b33b999438692a91be275bb89cdc7e401f448", [:mix], [{:gen_state_machine, "~> 2.0", [hex: :gen_state_machine, repo: "hexpm", optional: false]}, {:libring, "~> 1.0", [hex: :libring, repo: "hexpm", optional: false]}], "hexpm", "94884f84783fc1ba027aba8fe8a7dae4aad78c98e9f9c76667ec3471585c08c6"}, "telemetry": {:hex, :telemetry, "0.4.3", "a06428a514bdbc63293cd9a6263aad00ddeb66f608163bdec7c8995784080818", [:rebar3], [], "hexpm", "eb72b8365ffda5bed68a620d1da88525e326cb82a75ee61354fc24b844768041"}, "telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"}, "telemetry_poller": {:hex, :telemetry_poller, "0.5.1", "21071cc2e536810bac5628b935521ff3e28f0303e770951158c73eaaa01e962a", [:rebar3], [{:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4cab72069210bc6e7a080cec9afffad1b33370149ed5d379b81c7c5f0c663fd4"},