MishkaInstaller.Installer.Installer (Mishka installer v0.1.6)

Copy Markdown View Source

When it comes to Erlang and Elixir, the process of runtime installing and runtime uninstalling a new library or runtime upgrading it is subject to a number of constraints.

These restrictions can be implemented based on specific strategies and under specific conditions.

Please take note that this is not about hot coding, which refers to the process of updating a module that was developed using GenServer.

On account of this objective, a number of action functions have been incorporated into this module in order to make it possible for this task to be completed for you in accordance with some established strategies.

Pre-built ebin only (works in a release)

This module installs a pre-built library, i.e. its already compiled ebin (the ebin/*.beam files and the ebin/<app>.app resource). It does not compile at runtime.

Runtime compilation (mix deps.get/deps.compile/compile) is intentionally not supported, because a production release ships no Mix, no Hex, no project source and no _build tree, so there is nothing to compile with. Instead the artifacts are placed in a writable extensions directory, added to the code path and loaded:

Build the artifacts on a machine that uses the same Erlang/OTP and Elixir as the host.

Security considerations

It is important to remember that all of the functionalities contained within this section must be implemented at the high access level, and they should not directly take any input from the user. Ensure that you include the required safety measures.

Use cases information

Especially when you want to work with a library that depends on a large number of other libraries or vice versa, each of the functions of this file has its own requirements that must be taken into consideration.

Note:

If you are using Phoenix as developer mode, please disable live_reload in dev.exs. Please add reloadable_apps: [:mishka_installer] to your endpoint config in config.exs file.

Summary

Functions

This function is the same as the install/1 function, with the difference that it executes one by one in a simple queue (MishkaInstaller.Installer.CompileHandler).

To delete a runtime library from Mnesia database by id or name.

To drop all runtime libraries from Mnesia database.

A sample %MishkaInstaller.Installer.Installer{} populated from defaults + type-based fallbacks.

To get all runtime libraries information from Mnesia database.

To get a runtime library information from Mnesia database by id.

To get a runtime library information from Mnesia database by App name.

To get all runtime libraries ids from Mnesia database.

This function activates a pre-built library (its compiled ebin) within the running system. It is possible that this library is already present on the system, or it may serve as an update to the version that was previously available.

This function deactivates a library from runtime and removes its pre-built package from the extensions directory.

To check is a runtime library unique or not in Mnesia database.

This function is exactly like unique/2 function, except that its output is a Boolean.

To Add or edit a runtime library information from Mnesia database.

To edit a specific field/fields of a runtime library from the Mnesia database.

Types

builder_entry()

@type builder_entry() :: {:root, struct() | map(), :edit} | struct() | map()

download_type()

@type download_type() :: :path | :url | :github_tag | :github_latest_release

error_return()

@type error_return() ::
  {:error, [%{action: atom(), field: atom(), message: String.t()}]}

okey_return()

@type okey_return() :: {:ok, struct() | map() | module() | [any()]}

t()

@type t() :: %MishkaInstaller.Installer.Installer{
  app: String.t(),
  asset: String.t() | nil,
  checksum: String.t() | nil,
  depends: [String.t()] | nil,
  id: MishkaInstaller.Helper.UUID.t() | nil,
  inserted_at: DateTime.t() | nil,
  path: String.t(),
  prepend_paths: [String.t()] | nil,
  tag: String.t() | nil,
  type: download_type() | nil,
  updated_at: DateTime.t() | nil,
  version: String.t()
}

Functions

async_install(app)

@spec async_install(t()) :: error_return() | :ok

This function is the same as the install/1 function, with the difference that it executes one by one in a simple queue (MishkaInstaller.Installer.CompileHandler).

Security considerations

It is important to remember that all of the functionalities contained within this section must be implemented at the high access level, and they should not directly take any input from the user. Ensure that you include the required safety measures.

Example:

alias MishkaInstaller.Installer.Installer

{:ok, lib} = Installer.builder(%{
  app: "mishka_developer_tools",
  version: "0.1.6",
  path: "<extensions_path>/mishka_developer_tools-0.1.6"
})

Installer.async_install(lib)

builder(attrs_or_input, error \\ false)

delete(field, value)

@spec delete(atom(), String.t()) :: error_return() | okey_return()

To delete a runtime library from Mnesia database by id or name.

Security considerations

It is important to remember that all of the functionalities contained within this section must be implemented at the high access level, and they should not directly take any input from the user. Ensure that you include the required safety measures.

Example:

delete(:app, "mishka_developer_tools")

delete(:id, "c63aea42-209a-40fb-b5c6-a0d28ee7e25b")

drop()

@spec drop() :: {:ok, :atomic} | {:error, any(), charlist()}

To drop all runtime libraries from Mnesia database.

Security considerations

It is important to remember that all of the functionalities contained within this section must be implemented at the high access level, and they should not directly take any input from the user. Ensure that you include the required safety measures.

Example:

drop()

enforce_keys()

enforce_keys(field)

example()

A sample %MishkaInstaller.Installer.Installer{} populated from defaults + type-based fallbacks.

get()

@spec get() :: [map() | struct()]

To get all runtime libraries information from Mnesia database.

Security considerations

It is important to remember that all of the functionalities contained within this section must be implemented at the high access level, and they should not directly take any input from the user. Ensure that you include the required safety measures.

Example:

get()

get(id)

@spec get(String.t()) :: struct() | nil

To get a runtime library information from Mnesia database by id.

Security considerations

It is important to remember that all of the functionalities contained within this section must be implemented at the high access level, and they should not directly take any input from the user. Ensure that you include the required safety measures.

Example:

get("c63aea42-209a-40fb-b5c6-a0d28ee7e25b")

get(field, value)

@spec get(:app, String.t()) :: struct() | nil

To get a runtime library information from Mnesia database by App name.

Security considerations

It is important to remember that all of the functionalities contained within this section must be implemented at the high access level, and they should not directly take any input from the user. Ensure that you include the required safety measures.

Example:

get(:app, "mishka_developer_tools")

ids()

@spec ids() :: [String.t()]

To get all runtime libraries ids from Mnesia database.

Security considerations

It is important to remember that all of the functionalities contained within this section must be implemented at the high access level, and they should not directly take any input from the user. Ensure that you include the required safety measures.

Example:

ids()

install(app)

@spec install(t()) :: error_return() | okey_return()

This function activates a pre-built library (its compiled ebin) within the running system. It is possible that this library is already present on the system, or it may serve as an update to the version that was previously available.

The package is a ebin directory plus a ebin/<app>.app resource. The type decides how it is obtained: :path uses a local package already inside the extensions directory (see MishkaInstaller.Installer.LibraryHandler.extensions_path/0); :url/:github_tag/ :github_latest_release download a pre-built tar.gz artifact and extract it there. Nothing is ever compiled here (see MishkaInstaller.Installer.Downloader).

The steps are: validate the app name, resolve the package (local move or download + extract), confirm the ebin/.app exist, reject a same/older version that is already running, add the ebin to the code path (Code.prepend_path/1), then Application.load/1 + Application.ensure_all_started/1. On any failure after the code path is touched, the partial install is rolled back so the node is left clean.

Security considerations

It is important to remember that all of the functionalities contained within this section must be implemented at the high access level, and they should not directly take any input from the user. Ensure that you include the required safety measures.

Loading a .beam is running arbitrary code with full node privileges; the BEAM has no sandbox. Only install artifacts from a trusted source, built for the same Erlang/OTP and Elixir as the host.

In reality, the structure of this module __MODULE__.builder/1, which likewise possesses a high access level validation, is what this function takes as its input.

For read more please see this type MishkaInstaller.Installer.Installer.t().

Example:

alias MishkaInstaller.Installer.Installer

# `path` points to the pre-built package placed inside the extensions directory.
{:ok, lib} = Installer.builder(%{
  app: "mishka_developer_tools",
  version: "0.1.6",
  path: "<extensions_path>/mishka_developer_tools-0.1.6"
})

Installer.install(lib)

keys()

keys(field)

uninstall(app)

@spec uninstall(t() | map()) :: :ok | error_return()

This function deactivates a library from runtime and removes its pre-built package from the extensions directory.

It stops and unloads the application, drops its ebin from the code path, deletes the "<app>-<version>" directory and removes the record from the database.

Note that the sub-set libraries are not removed by this function.

In later versions, a checker to delete sub-app libraries might be included. You can add it as a custom for now.

Security considerations

It is important to remember that all of the functionalities contained within this section must be implemented at the high access level, and they should not directly take any input from the user. Ensure that you include the required safety measures.

alias MishkaInstaller.Installer.Installer

# Normal calling
Installer.uninstall(%__MODULE__{app: "some_name", version: "0.1.0", path: "some_path"})

unique(field, value)

@spec unique(:app, String.t()) :: :ok | error_return()

To check is a runtime library unique or not in Mnesia database.

It returns :ok, or {:error, reason}. Note that if the requested runtime library does not exist, It means it is unique, and if it is already in the database, it means it is not unique

Security considerations

It is important to remember that all of the functionalities contained within this section must be implemented at the high access level, and they should not directly take any input from the user. Ensure that you include the required safety measures.

Example:

unique(:app, "mishka_developer_tools")

unique?(field, value)

@spec unique?(:app, String.t()) :: boolean()

This function is exactly like unique/2 function, except that its output is a Boolean.

Security considerations

It is important to remember that all of the functionalities contained within this section must be implemented at the high access level, and they should not directly take any input from the user. Ensure that you include the required safety measures.

Example:

unique?(:app, "mishka_developer_tools")

write(data)

@spec write(builder_entry()) :: error_return() | okey_return()

To Add or edit a runtime library information from Mnesia database.

Security considerations

It is important to remember that all of the functionalities contained within this section must be implemented at the high access level, and they should not directly take any input from the user. Ensure that you include the required safety measures.

Example:

data = %{app: "uniq", version: "0.6.1", tag: "0.6.1", type: :hex, path: "uniq"}
write(data)

write(field, value, updated_to)

@spec write(atom(), String.t(), map()) :: error_return() | okey_return()

To edit a specific field/fields of a runtime library from the Mnesia database.

The first input can only be name and ID [:app].

Security considerations

It is important to remember that all of the functionalities contained within this section must be implemented at the high access level, and they should not directly take any input from the user. Ensure that you include the required safety measures.

Example:

write(:app, "mishka_developer_tools", %{type: :hex})

write(:id, "c63aea42-209a-40fb-b5c6-a0d28ee7e25b", %{type: :hex})