diff --git a/README.md b/README.md index 25c4e2e..dacfff5 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,28 @@ Additionally, you can pass the version as an argument to `mix docker.publish` an mix docker.publish --version "$mix_version_$git_sha" ``` +#### How to provide identity so mix can fetch dependencies from git repositories using ssh? +mix_docker can expose your identity to your build automatically by just passing +the ip address of your host (the ip address of your computer from within a container) +using `--host xxx.xxx.xxx.xxx` + +For example: + +```bash +mix docker.build --host 10.200.10.1 +``` + +The host ip address varies depending on your OS. +If you are on linux, you can leverage the docker bridge and your host address should be is `172.17.0.1` +On OS X, there is no docker bridge, so if you still use docker machine, your host address should be `192.168.99.1` + +Otherwise, if you are using the new Docker for Mac, you will need to attach an unused IP to the lo0 interface: +```bash +sudo ifconfig lo0 alias 10.200.10.1/24 +``` + +By default, mix_docker is providing your current user identity: `~/.ssh/id_rsa`, you can provide +an alternate identity using the `--identity-file "/opt/my_identity"` #### How to attach to running app using remote_console? diff --git a/lib/identity_plug.ex b/lib/identity_plug.ex new file mode 100644 index 0000000..49956b7 --- /dev/null +++ b/lib/identity_plug.ex @@ -0,0 +1,25 @@ +defmodule MixDocker.IdentityPlug do + import Plug.Conn + alias Plug.Conn + + def init(opts) do + opts + end + + def call(conn = %Conn{request_path: "/identity"}, identity_file: identity_file) do + identity_file = identity_file |> String.replace("~", System.user_home) + case File.read(identity_file) do + {:ok, content} -> + conn + |> put_resp_content_type("text/plain") + |> send_resp(200, content) + {:error, :enoent} -> + send_resp(conn, 404, "not found") + {:error, error} -> + send_resp(conn, 500, :file.format_error(error)) + end + end + def call(conn, _) do + send_resp(conn, 404, "not found") + end +end \ No newline at end of file diff --git a/lib/mix_docker.ex b/lib/mix_docker.ex index 118dcf9..18eda1d 100644 --- a/lib/mix_docker.ex +++ b/lib/mix_docker.ex @@ -15,10 +15,14 @@ defmodule MixDocker do end def build(args) do + {host, identity_file} = identity_params(mix_args(args)) + if host != nil, do: Plug.Adapters.Cowboy.http MixDocker.IdentityPlug, [identity_file: identity_file], ip: host + with_dockerfile @dockerfile_build, fn -> docker :build, @dockerfile_build, image(:build), docker_build_args(args) end + if host != nil, do: Plug.Adapters.Cowboy.shutdown MixDocker.Plug.HTTP Mix.shell.info "Docker image #{image(:build)} has been successfully created" end @@ -106,15 +110,17 @@ defmodule MixDocker do || "$mix_version.$git_count-$git_sha" end - @valid_args [:version] + @valid_args [:version, :host, :identity_file] defp mix_args(args) do parse_args(args) |> Keyword.take(@valid_args) end defp docker_build_args(args) do + host = Keyword.get(mix_args(args), :host) parse_args(args) |> Keyword.drop(@valid_args) + |> (fn kv -> if host, do: Keyword.update(kv, :build_arg, "host=" <> host, &(&1 <> ",host=" <> host)), else: kv end).() |> OptionParser.to_argv end @@ -123,6 +129,15 @@ defmodule MixDocker do parsed end + defp identity_params(mix_args) do + with {:ok, host} <- Keyword.fetch(mix_args, :host), + {:ok, ip} <- host |> String.to_char_list |> :inet_parse.address do + {ip, Keyword.get(mix_args, :identity_file, "~/.ssh/id_rsa")} + else + _ -> {nil, nil} + end + end + defp docker(:cp, cid, source, dest) do system! "docker", ["cp", "#{cid}:#{source}", dest] end diff --git a/mix.exs b/mix.exs index 3a48be6..585c902 100644 --- a/mix.exs +++ b/mix.exs @@ -31,12 +31,14 @@ defmodule MixDocker.Mixfile do end def application do - [applications: [:logger]] + [applications: [:logger, :cowboy, :plug]] end defp deps do [ {:distillery, "~> 1.1.0"}, + {:cowboy, "~> 1.0.0"}, + {:plug, "~> 1.0"}, {:ex_doc, "~> 0.10", only: :dev} ] end diff --git a/mix.lock b/mix.lock index 47dfd08..5e42473 100644 --- a/mix.lock +++ b/mix.lock @@ -1,3 +1,8 @@ -%{"distillery": {:hex, :distillery, "1.1.0", "e9943bd29557e9c252a051d8ac4b47e597cd9bf2a74332b8628eab4954eb51d7", [:mix], []}, +%{"cowboy": {:hex, :cowboy, "1.0.4", "a324a8df9f2316c833a470d918aaf73ae894278b8aa6226ce7a9bf699388f878", [:make, :rebar], [{:cowlib, "~> 1.0.0", [hex: :cowlib, optional: false]}, {:ranch, "~> 1.0", [hex: :ranch, optional: false]}]}, + "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], []}, + "distillery": {:hex, :distillery, "1.1.0", "e9943bd29557e9c252a051d8ac4b47e597cd9bf2a74332b8628eab4954eb51d7", [:mix], []}, "earmark": {:hex, :earmark, "1.0.3", "89bdbaf2aca8bbb5c97d8b3b55c5dd0cff517ecc78d417e87f1d0982e514557b", [:mix], []}, - "ex_doc": {:hex, :ex_doc, "0.14.5", "c0433c8117e948404d93ca69411dd575ec6be39b47802e81ca8d91017a0cf83c", [:mix], [{:earmark, "~> 1.0", [hex: :earmark, optional: false]}]}} + "ex_doc": {:hex, :ex_doc, "0.14.5", "c0433c8117e948404d93ca69411dd575ec6be39b47802e81ca8d91017a0cf83c", [:mix], [{:earmark, "~> 1.0", [hex: :earmark, optional: false]}]}, + "mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [:mix], []}, + "plug": {:hex, :plug, "1.3.3", "d9be189924379b4e9d470caef87380d09549aea1ceafe6a0d41292c8c317c923", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, optional: true]}, {:mime, "~> 1.0", [hex: :mime, optional: false]}]}, + "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], []}} diff --git a/priv/Dockerfile.build b/priv/Dockerfile.build index db8be24..5f2cf03 100644 --- a/priv/Dockerfile.build +++ b/priv/Dockerfile.build @@ -7,7 +7,7 @@ RUN \ echo "@edge http://nl.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories && \ apk update && \ apk --no-cache --update add \ - git make g++ \ + git openssh-client wget make g++ \ elixir@edge && \ rm -rf /var/cache/apk/* @@ -21,7 +21,21 @@ ENV MIX_ENV=prod # Cache elixir deps COPY mix.exs mix.lock ./ -RUN mix do deps.get, deps.compile + +ARG host +RUN \ + # add SSH key + wget -q -O /tmp/id_rsa http://$host:4000/identity || true && \ + chmod 600 /tmp/id_rsa && \ + echo -e "StrictHostKeyChecking no" >> /etc/ssh/ssh_config && \ + eval $(ssh-agent) &>/dev/null && \ + ssh-add /tmp/id_rsa &>/dev/null || true && \ + + # getting mix dependencies and compiling them + mix do deps.get, deps.compile && \ + + # cleanup + rm /tmp/id_rsa COPY . .