I’m trying to create a command line utility for creating administrative users in a back office application.
defmodule Admin.CLI do
def main(args) do
handle_command(args)
end
def handle_command(["createadmin" | rest]) do
{opts, _, _} = OptionParser.parse(rest, switches: [username: :string, password: :string],
aliases: [u: :username, p: :password])
username = Keyword.get(opts, :username) || raise "You must supply a username"
password = Keyword.get(opts, :password) || raise "You must supply a password"
case Admin.create_user(%{"username" => username, "password" => password}) do
{:ok, _user} ->
IO.puts("User #{username} created with password #{password}")
{:error, _} ->
IO.puts(:stderr, "Error creating user.")
end
end
def handle_command(_) do
IO.puts(:stderr, "Invalid command.")
end
end
But this is the output that I receive when running the command:
$ ./admin createadmin --username test --password test
[warn] The on_load function for module Elixir.Argon2.Base returned:
{:function_clause, [{:filename, :join, [{:error, :bad_name}, 'argon2_nif'], [file: 'filename.erl', line: 446]}, {Argon2.Base, :load_nif, 0, [file: 'lib/argon2/base.ex', line: 115]}, {Argon2.Base, :init, 0, [file: 'lib/argon2/base.ex', ...]}, {:code_server, :"-handle_on_load/5-fun-0-", 1, [...]}]}
[error] Process #PID<0.243.0> raised an exception
** (FunctionClauseError) no function clause matching in :filename.join/2
(stdlib) filename.erl:446: :filename.join({:error, :bad_name}, 'argon2_nif')
(argon2_elixir) lib/argon2/base.ex:115: Argon2.Base.load_nif/0
(argon2_elixir) lib/argon2/base.ex:14: Argon2.Base.init/0
(kernel) code_server.erl:1340: anonymous fn/1 in :code_server.handle_on_load/5
** (UndefinedFunctionError) function Argon2.Base.hash_password/3 is undefined (module Argon2.Base is not available)
(argon2_elixir) Argon2.Base.hash_password("test", <<158, 41, 41, 255, 119, 126, 67, 223, 90, 157, 199, 83, 101, 28, 67, 206>>, [])
(argon2_elixir) lib/argon2.ex:140: Argon2.add_hash/2
(admin) lib/admin/user.ex:30: Admin.User.maybe_add_hash/1
(admin) lib/admin.ex:14: Admin.create_user/1
(admin) lib/admin/cli.ex:14: Admin.CLI.handle_command/1
(elixir) lib/kernel/cli.ex:121: anonymous fn/3 in Kernel.CLI.exec_fun/2
When running with the same input from iex -S mix
, it works as expected:
iex(1)> Admin.CLI.handle_command(["createadmin", "--username", "test", "--password", "test"])
User test created with password test
[debug] QUERY OK db=25.1ms decode=0.8ms queue=0.6ms
INSERT INTO "users" ("password_hash","username") VALUES ($1,$2) RETURNING "id" ["$argon2id$v=19$m=131072,t=8,p=4$dXaxDD2S7LG2sAaxfj8u9w$iJw1bEMSwZCPS3jIFZZ4Fzg/5sGf0hSEh8izr1hlyCY", "test"]
Is my approach wrong here? Should the escript just be sending messages to a handler process that’s already running inside the application?